Cleanup animation

This commit is contained in:
2026-05-08 15:44:55 -05:00
parent 6d876bb767
commit 73e73d8772
20 changed files with 578 additions and 445 deletions
+1
View File
@@ -7,4 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
easing.c
animation.c
animationproperty.c
)
+37 -54
View File
@@ -6,91 +6,74 @@
#include "animation.h"
#include "assert/assert.h"
#include "util/memory.h"
#include <math.h>
#include "util/math.h"
void animationInit(animation_t *anim) {
memoryZero(anim, sizeof(animation_t));
}
animationproperty_t *animationAddProperty(animation_t *anim, float_t *target) {
animationproperty_t *animationAddProperty(animation_t *anim) {
assertTrue(
anim->propertyCount < ANIMATION_PROPERTY_COUNT_MAX,
"Property count exceeds ANIMATION_PROPERTY_COUNT_MAX"
);
animationproperty_t *prop = &anim->properties[anim->propertyCount++];
prop->target = target;
prop->keyframeCount = 0;
return prop;
}
void animationPropertyAddKeyframe(
animation_t *anim,
animationproperty_t *prop,
float_t time,
float_t value,
easingtype_t easing
) {
assertTrue(
prop->keyframeCount < ANIMATION_KEYFRAME_COUNT_MAX,
"Keyframe count exceeds ANIMATION_KEYFRAME_COUNT_MAX"
);
uint8_t i = prop->keyframeCount++;
prop->keyframes[i].time = time;
prop->keyframes[i].value = value;
prop->keyframes[i].easing = easing;
if(time > anim->duration) anim->duration = time;
}
static float_t animationPropertyGetValue(
const animationproperty_t *prop,
float_t time
) {
if(prop->keyframeCount == 0) return 0.0f;
uint8_t last = prop->keyframeCount - 1;
if(prop->keyframeCount == 1) return prop->keyframes[0].value;
if(time <= prop->keyframes[0].time) return prop->keyframes[0].value;
if(time >= prop->keyframes[last].time) return prop->keyframes[last].value;
for(uint8_t i = 0; i < last; i++) {
const keyframe_t *a = &prop->keyframes[i];
const keyframe_t *b = &prop->keyframes[i + 1];
if(time < a->time || time >= b->time) continue;
float_t t = (time - a->time) / (b->time - a->time);
t = easingApply(a->easing, t);
return a->value + (b->value - a->value) * t;
}
return prop->keyframes[last].value;
}
void animationUpdate(animation_t *anim, float_t delta) {
assertNotNull(anim, "Animation cannot be null");
// Anim done?
if(
(anim->flags & ANIMATION_FLAG_FINISHED) &&
!(anim->flags & ANIMATION_FLAG_LOOP_ENABLED)
) return;
// Start
anim->flags |= ANIMATION_FLAG_STARTED;
anim->time += delta;
if(anim->duration > 0.0f && anim->time >= anim->duration) {
// Tick
float_t duration = animationGetDuration(anim);
if(anim->time >= duration) {
// Done/Loop
if(anim->flags & ANIMATION_FLAG_LOOP_ENABLED) {
anim->time = fmodf(anim->time, anim->duration);
anim->time = mathModFloat(anim->time, duration);
} else {
anim->time = anim->duration;
anim->time = duration;
anim->flags |= ANIMATION_FLAG_FINISHED;
}
}
for(uint8_t i = 0; i < anim->propertyCount; i++) {
animationproperty_t *prop = &anim->properties[i];
if(!prop->target) continue;
*prop->target = animationPropertyGetValue(prop, anim->time);
}
}
void animationReset(animation_t *anim) {
anim->time = 0.0f;
anim->flags &= ~(ANIMATION_FLAG_FINISHED | ANIMATION_FLAG_STARTED);
}
bool_t animationIsFinished(const animation_t *anim) {
return (anim->flags & ANIMATION_FLAG_FINISHED) != 0;
}
bool_t animationIsStarted(const animation_t *anim) {
return (anim->flags & ANIMATION_FLAG_STARTED) != 0;
}
bool_t animationIsLooping(const animation_t *anim) {
return (anim->flags & ANIMATION_FLAG_LOOP_ENABLED) != 0;
}
float_t animationGetDuration(const animation_t *anim) {
float_t duration = 0.0f;
for(uint8_t i = 0; i < anim->propertyCount; i++) {
animationproperty_t *prop = &anim->properties[i];
if(prop->keyframeCount == 0) continue;
float_t lastKeyframeTime =
prop->keyframes[prop->keyframeCount - 1].time;
if(lastKeyframeTime > duration) duration = lastKeyframeTime;
}
return duration;
}
+48 -37
View File
@@ -4,72 +4,83 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "keyframe.h"
#include "error/error.h"
#include "animationproperty.h"
#define ANIMATION_PROPERTY_COUNT_MAX 8
typedef uint8_t animationflags_t;
#define ANIMATION_FLAG_FINISHED ((animationflags_t)(1 << 0))
#define ANIMATION_FLAG_STARTED ((animationflags_t)(1 << 1))
#define ANIMATION_FLAG_LOOP_ENABLED ((animationflags_t)(1 << 2))
typedef struct {
float_t *target;
keyframe_t keyframes[ANIMATION_KEYFRAME_COUNT_MAX];
uint8_t keyframeCount;
} animationproperty_t;
#define ANIMATION_FLAG_FINISHED (1 << 0)
#define ANIMATION_FLAG_STARTED (1 << 1)
#define ANIMATION_FLAG_LOOP_ENABLED (1 << 2)
typedef struct {
animationproperty_t properties[ANIMATION_PROPERTY_COUNT_MAX];
uint8_t propertyCount;
float_t time;
float_t duration;
animationflags_t flags;
uint8_t flags;
} animation_t;
/**
* Zeroes an animation struct.
* Initializes an animation.
*
* @param anim The animation to initialize.
*/
void animationInit(animation_t *anim);
/**
* Adds a new animated property. The caller owns target and must keep it valid
* for the lifetime of the animation.
*
* @param anim The animation to add the property to.
* @return A pointer to the new property.
*/
animationproperty_t *animationAddProperty(animation_t *anim, float_t *target);
/**
* Appends a keyframe to a property. Keyframes must be added in ascending time
* order. Updates the animation's duration.
*/
void animationPropertyAddKeyframe(
animation_t *anim,
animationproperty_t *prop,
float_t time,
float_t value,
easingtype_t easing
);
animationproperty_t *animationAddProperty(animation_t *anim);
/**
* Advances the animation by delta seconds and writes interpolated values to
* each property's target pointer.
*
* @param anim The animation to update.
* @param delta The time to advance the animation by, in seconds.
*/
void animationUpdate(animation_t *anim, float_t delta);
/**
* Resets the animation to the beginning, clearing Started and Finished flags.
*
* @param anim The animation to reset.
*/
void animationReset(animation_t *anim);
static inline bool_t animationIsFinished(const animation_t *anim) {
return (anim->flags & ANIMATION_FLAG_FINISHED) != 0;
}
/**
* Returns true if the animation has finished (i.e. reached the end of its
* duration and is not looping).
*
* @param anim The animation to check.
* @return true if the animation has finished, false otherwise.
*/
bool_t animationIsFinished(const animation_t *anim);
static inline bool_t animationIsStarted(const animation_t *anim) {
return (anim->flags & ANIMATION_FLAG_STARTED) != 0;
}
/**
* Returns true if the animation has been started (i.e. animationUpdate has
* been called at least once).
*
* @param anim The animation to check.
* @return true if the animation has been started, false otherwise.
*/
bool_t animationIsStarted(const animation_t *anim);
static inline bool_t animationIsLooping(const animation_t *anim) {
return (anim->flags & ANIMATION_FLAG_LOOP_ENABLED) != 0;
}
/**
* Returns true if the animation is set to loop.
*
* @param anim The animation to check.
* @return true if the animation is set to loop, false otherwise.
*/
bool_t animationIsLooping(const animation_t *anim);
/**
* Gets the total duration of the animation (based on the keyframes).
*
* @param anim The animation to get the duration of.
* @return The total duration of the animation, in seconds.
*/
float_t animationGetDuration(const animation_t *anim);
+55
View File
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "animationproperty.h"
#include "assert/assert.h"
void animationPropertyAddKeyframe(
animationproperty_t *prop,
const float_t time,
const float_t value,
const easingtype_t easing
) {
assertNotNull(prop, "Property cannot be null");
assertTrue(
prop->keyframeCount < ANIMATION_PROPERTY_KEYFRAME_COUNT_MAX,
"Too many keyframes added to property"
);
assertTrue(time >= 0.0f, "Keyframe time cannot be negative");
keyframe_t *frame = &prop->keyframes[prop->keyframeCount++];
frame->time = time;
frame->value = value;
frame->easing = easing;
}
float_t animationPropertyGetValue(
const animationproperty_t *prop,
const float_t time
) {
assertNotNull(prop, "Property cannot be null");
assertTrue(time >= 0.0f, "Time cannot be negative");
if(prop->keyframeCount == 0) return 0.0f;
uint8_t last = prop->keyframeCount - 1;
if(prop->keyframeCount == 1) return prop->keyframes[0].value;
if(time <= prop->keyframes[0].time) return prop->keyframes[0].value;
if(time >= prop->keyframes[last].time) return prop->keyframes[last].value;
for(uint8_t i = 0; i < last; i++) {
const keyframe_t *a = &prop->keyframes[i];
const keyframe_t *b = &prop->keyframes[i + 1];
if(time < a->time || time >= b->time) continue;
float_t t = (time - a->time) / (b->time - a->time);
t = easingApply(a->easing, t);
return a->value + (b->value - a->value) * t;
}
return prop->keyframes[last].value;
}
+44
View File
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "keyframe.h"
#define ANIMATION_PROPERTY_KEYFRAME_COUNT_MAX 16
typedef struct {
keyframe_t keyframes[ANIMATION_PROPERTY_KEYFRAME_COUNT_MAX];
uint8_t keyframeCount;
} animationproperty_t;
/**
* Appends a keyframe to a property. Keyframes must be added in ascending time
* order. Updates the animation's duration.
*
* @param property The property to add the keyframe to.
* @param time The time of the keyframe, in seconds.
* @param value The value of the keyframe.
* @param easing The easing type to use when interpolating to the next keyframe.
*/
void animationPropertyAddKeyframe(
animationproperty_t *property,
const float_t time,
const float_t value,
const easingtype_t easing
);
/**
* Gets the property's value at a given time.
*
* @param prop The property to get the value from.
* @param time The time at which to get the value, in seconds.
* @return The value of the property at the given time.
*/
float_t animationPropertyGetValue(
const animationproperty_t *prop,
const float_t time
);
+39 -34
View File
@@ -7,82 +7,101 @@
#include "assert/assert.h"
#include <math.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)
const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = {
easingLinear,
easingInSine,
easingOutSine,
easingInOutSine,
easingInQuad,
easingOutQuad,
easingInOutQuad,
easingInCubic,
easingOutCubic,
easingInOutCubic,
easingInQuart,
easingOutQuart,
easingInOutQuart,
easingInBack,
easingOutBack,
easingInOutBack,
};
float_t easingLinear(float_t t) {
float_t easingApply(const easingtype_t type, const float_t t) {
assertTrue(type < EASING_COUNT, "Invalid easing type");
return EASING_FUNCTIONS[type](t);
}
float_t easingLinear(const float_t t) {
return t;
}
float_t easingInSine(float_t t) {
float_t easingInSine(const float_t t) {
return 1.0f - cosf(t * EASING_PI * 0.5f);
}
float_t easingOutSine(float_t t) {
float_t easingOutSine(const float_t t) {
return sinf(t * EASING_PI * 0.5f);
}
float_t easingInOutSine(float_t t) {
float_t easingInOutSine(const float_t t) {
return -(cosf(EASING_PI * t) - 1.0f) * 0.5f;
}
float_t easingInQuad(float_t t) {
float_t easingInQuad(const float_t t) {
return t * t;
}
float_t easingOutQuad(float_t t) {
float_t easingOutQuad(const float_t t) {
float_t u = 1.0f - t;
return 1.0f - u * u;
}
float_t easingInOutQuad(float_t t) {
float_t easingInOutQuad(const float_t t) {
if(t < 0.5f) return 2.0f * t * t;
float_t u = -2.0f * t + 2.0f;
return 1.0f - u * u * 0.5f;
}
float_t easingInCubic(float_t t) {
float_t easingInCubic(const float_t t) {
return t * t * t;
}
float_t easingOutCubic(float_t t) {
float_t easingOutCubic(const float_t t) {
float_t u = 1.0f - t;
return 1.0f - u * u * u;
}
float_t easingInOutCubic(float_t t) {
float_t easingInOutCubic(const float_t t) {
if(t < 0.5f) return 4.0f * t * t * t;
float_t u = -2.0f * t + 2.0f;
return 1.0f - u * u * u * 0.5f;
}
float_t easingInQuart(float_t t) {
float_t easingInQuart(const float_t t) {
return t * t * t * t;
}
float_t easingOutQuart(float_t t) {
float_t easingOutQuart(const float_t t) {
float_t u = 1.0f - t;
return 1.0f - u * u * u * u;
}
float_t easingInOutQuart(float_t t) {
float_t easingInOutQuart(const float_t t) {
if(t < 0.5f) return 8.0f * t * t * t * t;
float_t u = -2.0f * t + 2.0f;
return 1.0f - u * u * u * u * 0.5f;
}
float_t easingInBack(float_t t) {
float_t easingInBack(const float_t t) {
return EASING_C3 * t * t * t - EASING_C1 * t * t;
}
float_t easingOutBack(float_t t) {
float_t easingOutBack(const float_t t) {
float_t u = t - 1.0f;
return 1.0f + EASING_C3 * u * u * u + EASING_C1 * u * u;
}
float_t easingInOutBack(float_t t) {
float_t easingInOutBack(const float_t t) {
if(t < 0.5f) {
float_t u = 2.0f * t;
return u * u * ((EASING_C2 + 1.0f) * u - EASING_C2) * 0.5f;
@@ -90,17 +109,3 @@ float_t easingInOutBack(float_t t) {
float_t u = 2.0f * t - 2.0f;
return (u * u * ((EASING_C2 + 1.0f) * u + EASING_C2) + 2.0f) * 0.5f;
}
const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = {
easingLinear,
easingInSine, easingOutSine, easingInOutSine,
easingInQuad, easingOutQuad, easingInOutQuad,
easingInCubic, easingOutCubic, easingInOutCubic,
easingInQuart, easingOutQuart, easingInOutQuart,
easingInBack, easingOutBack, easingInOutBack,
};
float_t easingApply(easingtype_t type, float_t t) {
assertTrue(type < EASING_COUNT, "Invalid easing type");
return EASING_FUNCTIONS[type](t);
}
+50 -36
View File
@@ -6,44 +6,58 @@
#pragma once
#include "dusk.h"
#define EASING_LINEAR 0
#define EASING_IN_SINE 1
#define EASING_OUT_SINE 2
#define EASING_IN_OUT_SINE 3
#define EASING_IN_QUAD 4
#define EASING_OUT_QUAD 5
#define EASING_IN_OUT_QUAD 6
#define EASING_IN_CUBIC 7
#define EASING_OUT_CUBIC 8
#define EASING_IN_OUT_CUBIC 9
#define EASING_IN_QUART 10
#define EASING_OUT_QUART 11
#define EASING_IN_OUT_QUART 12
#define EASING_IN_BACK 13
#define EASING_OUT_BACK 14
#define EASING_IN_OUT_BACK 15
#define EASING_COUNT 16
#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 uint8_t easingtype_t;
typedef float_t (*easingfn_t)(float_t t);
typedef enum {
EASING_LINEAR,
EASING_IN_SINE,
EASING_OUT_SINE,
EASING_IN_OUT_SINE,
EASING_IN_QUAD,
EASING_OUT_QUAD,
EASING_IN_OUT_QUAD,
EASING_IN_CUBIC,
EASING_OUT_CUBIC,
EASING_IN_OUT_CUBIC,
EASING_IN_QUART,
EASING_OUT_QUART,
EASING_IN_OUT_QUART,
EASING_IN_BACK,
EASING_OUT_BACK,
EASING_IN_OUT_BACK,
EASING_COUNT
} easingtype_t;
typedef float_t (*easingfn_t)(const float_t t);
extern const easingfn_t EASING_FUNCTIONS[EASING_COUNT];
float_t easingApply(easingtype_t type, float_t t);
/**
* Applies the specified easing function to t.
*
* @param type The easing type to apply.
* @param t The input time, in the range [0, 1].
* @return The eased value, in the range [0, 1].
*/
float_t easingApply(const easingtype_t type, const float_t t);
float_t easingLinear(float_t t);
float_t easingInSine(float_t t);
float_t easingOutSine(float_t t);
float_t easingInOutSine(float_t t);
float_t easingInQuad(float_t t);
float_t easingOutQuad(float_t t);
float_t easingInOutQuad(float_t t);
float_t easingInCubic(float_t t);
float_t easingOutCubic(float_t t);
float_t easingInOutCubic(float_t t);
float_t easingInQuart(float_t t);
float_t easingOutQuart(float_t t);
float_t easingInOutQuart(float_t t);
float_t easingInBack(float_t t);
float_t easingOutBack(float_t t);
float_t easingInOutBack(float_t t);
float_t easingLinear(const float_t t);
float_t easingInSine(const float_t t);
float_t easingOutSine(const float_t t);
float_t easingInOutSine(const float_t t);
float_t easingInQuad(const float_t t);
float_t easingOutQuad(const float_t t);
float_t easingInOutQuad(const float_t t);
float_t easingInCubic(const float_t t);
float_t easingOutCubic(const float_t t);
float_t easingInOutCubic(const float_t t);
float_t easingInQuart(const float_t t);
float_t easingOutQuart(const float_t t);
float_t easingInOutQuart(const float_t t);
float_t easingInBack(const float_t t);
float_t easingOutBack(const float_t t);
float_t easingInOutBack(const float_t t);
-2
View File
@@ -6,8 +6,6 @@
#pragma once
#include "easing.h"
#define ANIMATION_KEYFRAME_COUNT_MAX 16
typedef struct {
float_t time;
float_t value;
+2
View File
@@ -218,6 +218,8 @@
#endif
// Static Assertions
#define assertStructSize(struct, size) \
_Static_assert(sizeof(struct) == size, "Size of " #struct " must be " #size)
+2 -2
View File
@@ -25,7 +25,7 @@ errorret_t assetInit(void) {
}
bool_t assetFileExists(const char_t *filename) {
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long.");
assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long.");
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
if(idx < 0) return false;
@@ -38,7 +38,7 @@ errorret_t assetLoad(
void *params,
void *output
) {
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long.");
assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long.");
assertNotNull(output, "Output pointer cannot be NULL.");
assertNotNull(loader, "Asset file loader cannot be NULL.");
+4 -2
View File
@@ -16,9 +16,11 @@ errorret_t assetFileInit(
void *params,
void *output
) {
memoryZero(file, sizeof(assetfile_t));
assertNotNull(file, "Asset file cannot be NULL.");
assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long.");
file->filename = filename;
memoryZero(file, sizeof(assetfile_t));
memoryCopy(file->filename, filename, ASSET_FILE_NAME_MAX);
file->params = params;
file->output = output;
+2 -2
View File
@@ -9,14 +9,14 @@
#include "error/error.h"
#include <zip.h>
#define ASSET_FILE_PATH_MAX FILENAME_MAX
#define ASSET_FILE_NAME_MAX 48
typedef struct assetfile_s assetfile_t;
typedef errorret_t (*assetfileloader_t)(assetfile_t *file);
typedef struct assetfile_s {
const char_t *filename;
char_t filename[ASSET_FILE_NAME_MAX];
void *params;
void *output;
+3 -3
View File
@@ -168,7 +168,7 @@ errorret_t sceneSetImmediate(const char_t *scene) {
stringCopy(
SCENE.sceneNext,
scene == NULL ? "" : scene,
ASSET_FILE_PATH_MAX
ASSET_FILE_NAME_MAX
);
}
@@ -183,7 +183,7 @@ errorret_t sceneSetImmediate(const char_t *scene) {
stringCopy(
SCENE.sceneCurrent,
scene == NULL ? "" : scene,
ASSET_FILE_PATH_MAX
ASSET_FILE_NAME_MAX
);
if(scene != NULL) {
@@ -216,7 +216,7 @@ void sceneSet(const char_t *scene) {
stringCopy(
SCENE.sceneNext,
scene == NULL ? "" : scene,
ASSET_FILE_PATH_MAX
ASSET_FILE_NAME_MAX
);
}
+2 -2
View File
@@ -14,8 +14,8 @@
typedef struct {
bool_t sceneActive;
jerry_value_t scriptRef;
char_t sceneCurrent[ASSET_FILE_PATH_MAX];
char_t sceneNext[ASSET_FILE_PATH_MAX];
char_t sceneCurrent[ASSET_FILE_NAME_MAX];
char_t sceneNext[ASSET_FILE_NAME_MAX];
} scene_t;
extern scene_t SCENE;
+257 -257
View File
@@ -9,307 +9,307 @@
#include "animation/animation.h"
#include "util/memory.h"
static scriptproto_t MODULE_ANIMATION_PROTO;
// static scriptproto_t MODULE_ANIMATION_PROTO;
// JS animation wraps a single-property animation; value is the managed target.
typedef struct {
animation_t anim;
float_t value;
} jsAnimation_t;
// // JS animation wraps a single-property animation; value is the managed target.
// typedef struct {
// animation_t anim;
// float_t value;
// } jsAnimation_t;
static inline jsAnimation_t *moduleAnimationGetWrapper(
const jerry_call_info_t *callInfo
) {
return (jsAnimation_t *)scriptProtoGetValue(
&MODULE_ANIMATION_PROTO, callInfo->this_value
);
}
// static inline jsAnimation_t *moduleAnimationGetWrapper(
// const jerry_call_info_t *callInfo
// ) {
// return (jsAnimation_t *)scriptProtoGetValue(
// &MODULE_ANIMATION_PROTO, callInfo->this_value
// );
// }
// Returns the animation_t pointer (first field of jsAnimation_t — same address).
static inline animation_t *moduleAnimationGet(
const jerry_call_info_t *callInfo
) {
return (animation_t *)moduleAnimationGetWrapper(callInfo);
}
// // Returns the animation_t pointer (first field of jsAnimation_t — same address).
// static inline animation_t *moduleAnimationGet(
// const jerry_call_info_t *callInfo
// ) {
// return (animation_t *)moduleAnimationGetWrapper(callInfo);
// }
// Extracts an easingtype_t from a JS value. Accepts either a plain number
// or an Easing.xxx function (which has a .type numeric property).
static easingtype_t moduleAnimationReadEasing(
const jerry_value_t val
) {
if(jerry_value_is_number(val)) {
return (easingtype_t)(uint32_t)jerry_value_as_number(val);
}
if(jerry_value_is_object(val) || jerry_value_is_function(val)) {
jerry_value_t key = jerry_string_sz("type");
jerry_value_t typeVal = jerry_object_get(val, key);
jerry_value_free(key);
easingtype_t result = EASING_LINEAR;
if(jerry_value_is_number(typeVal)) {
result = (easingtype_t)(uint32_t)jerry_value_as_number(typeVal);
}
jerry_value_free(typeVal);
return result;
}
return EASING_LINEAR;
}
// // Extracts an easingtype_t from a JS value. Accepts either a plain number
// // or an Easing.xxx function (which has a .type numeric property).
// static easingtype_t moduleAnimationReadEasing(
// const jerry_value_t val
// ) {
// if(jerry_value_is_number(val)) {
// return (easingtype_t)(uint32_t)jerry_value_as_number(val);
// }
// if(jerry_value_is_object(val) || jerry_value_is_function(val)) {
// jerry_value_t key = jerry_string_sz("type");
// jerry_value_t typeVal = jerry_object_get(val, key);
// jerry_value_free(key);
// easingtype_t result = EASING_LINEAR;
// if(jerry_value_is_number(typeVal)) {
// result = (easingtype_t)(uint32_t)jerry_value_as_number(typeVal);
// }
// jerry_value_free(typeVal);
// return result;
// }
// return EASING_LINEAR;
// }
// Fires onReach callbacks for any keyframes crossed between prevTime and
// newTime. Returns an exception value on error, or jerry_undefined().
static jerry_value_t moduleAnimationFireKeyframes(
const jerry_value_t thisVal,
float_t prevTime,
float_t newTime
) {
jerry_value_t kfKey = jerry_string_sz("_keyframes");
jerry_value_t kfArr = jerry_object_get(thisVal, kfKey);
jerry_value_free(kfKey);
// // Fires onReach callbacks for any keyframes crossed between prevTime and
// // newTime. Returns an exception value on error, or jerry_undefined().
// static jerry_value_t moduleAnimationFireKeyframes(
// const jerry_value_t thisVal,
// float_t prevTime,
// float_t newTime
// ) {
// jerry_value_t kfKey = jerry_string_sz("_keyframes");
// jerry_value_t kfArr = jerry_object_get(thisVal, kfKey);
// jerry_value_free(kfKey);
if(!jerry_value_is_array(kfArr)) {
jerry_value_free(kfArr);
return jerry_undefined();
}
// if(!jerry_value_is_array(kfArr)) {
// jerry_value_free(kfArr);
// return jerry_undefined();
// }
jerry_length_t len = jerry_array_length(kfArr);
for(jerry_length_t i = 0; i < len; i++) {
jerry_value_t kfObj = jerry_object_get_index(kfArr, i);
// jerry_length_t len = jerry_array_length(kfArr);
// for(jerry_length_t i = 0; i < len; i++) {
// jerry_value_t kfObj = jerry_object_get_index(kfArr, i);
jerry_value_t tKey = jerry_string_sz("time");
jerry_value_t tVal = jerry_object_get(kfObj, tKey);
jerry_value_free(tKey);
float_t kfTime = (float_t)jerry_value_as_number(tVal);
jerry_value_free(tVal);
// jerry_value_t tKey = jerry_string_sz("time");
// jerry_value_t tVal = jerry_object_get(kfObj, tKey);
// jerry_value_free(tKey);
// float_t kfTime = (float_t)jerry_value_as_number(tVal);
// jerry_value_free(tVal);
if(prevTime < kfTime && newTime >= kfTime) {
jerry_value_t cbKey = jerry_string_sz("onReach");
jerry_value_t cb = jerry_object_get(kfObj, cbKey);
jerry_value_free(cbKey);
// if(prevTime < kfTime && newTime >= kfTime) {
// jerry_value_t cbKey = jerry_string_sz("onReach");
// jerry_value_t cb = jerry_object_get(kfObj, cbKey);
// jerry_value_free(cbKey);
if(jerry_value_is_function(cb)) {
jerry_value_t r = jerry_call(cb, thisVal, NULL, 0);
jerry_value_free(cb);
if(jerry_value_is_exception(r)) {
jerry_value_free(kfObj);
jerry_value_free(kfArr);
return r;
}
jerry_value_free(r);
} else {
jerry_value_free(cb);
}
}
// if(jerry_value_is_function(cb)) {
// jerry_value_t r = jerry_call(cb, thisVal, NULL, 0);
// jerry_value_free(cb);
// if(jerry_value_is_exception(r)) {
// jerry_value_free(kfObj);
// jerry_value_free(kfArr);
// return r;
// }
// jerry_value_free(r);
// } else {
// jerry_value_free(cb);
// }
// }
jerry_value_free(kfObj);
}
// jerry_value_free(kfObj);
// }
jerry_value_free(kfArr);
return jerry_undefined();
}
// jerry_value_free(kfArr);
// return jerry_undefined();
// }
// Fires onComplete once. Returns an exception on error or jerry_undefined().
static jerry_value_t moduleAnimationFireComplete(
const jerry_value_t thisVal
) {
jerry_value_t firedKey = jerry_string_sz("_completeFired");
jerry_value_t firedVal = jerry_object_get(thisVal, firedKey);
bool_t alreadyFired = jerry_value_is_true(firedVal);
jerry_value_free(firedVal);
// // Fires onComplete once. Returns an exception on error or jerry_undefined().
// static jerry_value_t moduleAnimationFireComplete(
// const jerry_value_t thisVal
// ) {
// jerry_value_t firedKey = jerry_string_sz("_completeFired");
// jerry_value_t firedVal = jerry_object_get(thisVal, firedKey);
// bool_t alreadyFired = jerry_value_is_true(firedVal);
// jerry_value_free(firedVal);
if(alreadyFired) {
jerry_value_free(firedKey);
return jerry_undefined();
}
// if(alreadyFired) {
// jerry_value_free(firedKey);
// return jerry_undefined();
// }
jerry_value_t trueVal = jerry_boolean(true);
jerry_object_set(thisVal, firedKey, trueVal);
jerry_value_free(firedKey);
jerry_value_free(trueVal);
// jerry_value_t trueVal = jerry_boolean(true);
// jerry_object_set(thisVal, firedKey, trueVal);
// jerry_value_free(firedKey);
// jerry_value_free(trueVal);
jerry_value_t cbKey = jerry_string_sz("onComplete");
jerry_value_t cb = jerry_object_get(thisVal, cbKey);
jerry_value_free(cbKey);
// jerry_value_t cbKey = jerry_string_sz("onComplete");
// jerry_value_t cb = jerry_object_get(thisVal, cbKey);
// jerry_value_free(cbKey);
if(jerry_value_is_function(cb)) {
jerry_value_t r = jerry_call(cb, thisVal, NULL, 0);
jerry_value_free(cb);
if(jerry_value_is_exception(r)) return r;
jerry_value_free(r);
} else {
jerry_value_free(cb);
}
// if(jerry_value_is_function(cb)) {
// jerry_value_t r = jerry_call(cb, thisVal, NULL, 0);
// jerry_value_free(cb);
// if(jerry_value_is_exception(r)) return r;
// jerry_value_free(r);
// } else {
// jerry_value_free(cb);
// }
return jerry_undefined();
}
// return jerry_undefined();
// }
moduleBaseFunction(moduleAnimationConstructor) {
jsAnimation_t *janim = (jsAnimation_t *)memoryAllocate(sizeof(jsAnimation_t));
animationInit(&janim->anim);
janim->value = 0.0f;
// moduleBaseFunction(moduleAnimationConstructor) {
// jsAnimation_t *janim = (jsAnimation_t *)memoryAllocate(sizeof(jsAnimation_t));
// animationInit(&janim->anim);
// janim->value = 0.0f;
if(argc > 0 && jerry_value_is_boolean(args[argc - 1])) {
if(jerry_value_is_true(args[argc - 1])) {
janim->anim.flags |= ANIMATION_FLAG_LOOP_ENABLED;
}
}
// if(argc > 0 && jerry_value_is_boolean(args[argc - 1])) {
// if(jerry_value_is_true(args[argc - 1])) {
// janim->anim.flags |= ANIMATION_FLAG_LOOP_ENABLED;
// }
// }
animationproperty_t *prop = animationAddProperty(&janim->anim, &janim->value);
// animationproperty_t *prop = animationAddProperty(&janim->anim, &janim->value);
if(argc > 0 && jerry_value_is_array(args[0])) {
jerry_length_t len = jerry_array_length(args[0]);
for(jerry_length_t i = 0; i < len; i++) {
jerry_value_t kf = jerry_object_get_index(args[0], i);
// if(argc > 0 && jerry_value_is_array(args[0])) {
// jerry_length_t len = jerry_array_length(args[0]);
// for(jerry_length_t i = 0; i < len; i++) {
// jerry_value_t kf = jerry_object_get_index(args[0], i);
jerry_value_t tKey = jerry_string_sz("time");
jerry_value_t vKey = jerry_string_sz("value");
jerry_value_t eKey = jerry_string_sz("easing");
// jerry_value_t tKey = jerry_string_sz("time");
// jerry_value_t vKey = jerry_string_sz("value");
// jerry_value_t eKey = jerry_string_sz("easing");
float_t t = (float_t)jerry_value_as_number(
jerry_object_get(kf, tKey)
);
float_t v = (float_t)jerry_value_as_number(
jerry_object_get(kf, vKey)
);
jerry_value_t eVal = jerry_object_get(kf, eKey);
easingtype_t e = moduleAnimationReadEasing(eVal);
// float_t t = (float_t)jerry_value_as_number(
// jerry_object_get(kf, tKey)
// );
// float_t v = (float_t)jerry_value_as_number(
// jerry_object_get(kf, vKey)
// );
// jerry_value_t eVal = jerry_object_get(kf, eKey);
// easingtype_t e = moduleAnimationReadEasing(eVal);
jerry_value_free(tKey);
jerry_value_free(vKey);
jerry_value_free(eKey);
jerry_value_free(eVal);
jerry_value_free(kf);
// jerry_value_free(tKey);
// jerry_value_free(vKey);
// jerry_value_free(eKey);
// jerry_value_free(eVal);
// jerry_value_free(kf);
animationPropertyAddKeyframe(&janim->anim, prop, t, v, e);
}
// animationPropertyAddKeyframe(&janim->anim, prop, t, v, e);
// }
// Store the JS keyframes array for onReach callback detection.
jerry_value_t kfKey = jerry_string_sz("_keyframes");
jerry_object_set(callInfo->this_value, kfKey, args[0]);
jerry_value_free(kfKey);
}
// // Store the JS keyframes array for onReach callback detection.
// jerry_value_t kfKey = jerry_string_sz("_keyframes");
// jerry_object_set(callInfo->this_value, kfKey, args[0]);
// jerry_value_free(kfKey);
// }
jerry_value_t firedKey = jerry_string_sz("_completeFired");
jerry_value_t falseVal = jerry_boolean(false);
jerry_object_set(callInfo->this_value, firedKey, falseVal);
jerry_value_free(firedKey);
jerry_value_free(falseVal);
// jerry_value_t firedKey = jerry_string_sz("_completeFired");
// jerry_value_t falseVal = jerry_boolean(false);
// jerry_object_set(callInfo->this_value, firedKey, falseVal);
// jerry_value_free(firedKey);
// jerry_value_free(falseVal);
jerry_object_set_native_ptr(
callInfo->this_value, &MODULE_ANIMATION_PROTO.info, janim
);
return jerry_undefined();
}
// jerry_object_set_native_ptr(
// callInfo->this_value, &MODULE_ANIMATION_PROTO.info, janim
// );
// return jerry_undefined();
// }
moduleBaseFunction(moduleAnimationUpdate) {
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
if(!janim) return moduleBaseThrow("Invalid Animation instance");
if(argc < 1 || !jerry_value_is_number(args[0])) {
return moduleBaseThrow("update() expects a number delta");
}
// moduleBaseFunction(moduleAnimationUpdate) {
// jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
// if(!janim) return moduleBaseThrow("Invalid Animation instance");
// if(argc < 1 || !jerry_value_is_number(args[0])) {
// return moduleBaseThrow("update() expects a number delta");
// }
float_t prevTime = janim->anim.time;
float_t delta = (float_t)jerry_value_as_number(args[0]);
animationUpdate(&janim->anim, delta);
// float_t prevTime = janim->anim.time;
// float_t delta = (float_t)jerry_value_as_number(args[0]);
// animationUpdate(&janim->anim, delta);
jerry_value_t kfResult = moduleAnimationFireKeyframes(
callInfo->this_value, prevTime, janim->anim.time
);
if(jerry_value_is_exception(kfResult)) return kfResult;
jerry_value_free(kfResult);
// jerry_value_t kfResult = moduleAnimationFireKeyframes(
// callInfo->this_value, prevTime, janim->anim.time
// );
// if(jerry_value_is_exception(kfResult)) return kfResult;
// jerry_value_free(kfResult);
if(animationIsFinished(&janim->anim)) {
jerry_value_t cResult = moduleAnimationFireComplete(
callInfo->this_value
);
if(jerry_value_is_exception(cResult)) return cResult;
jerry_value_free(cResult);
}
// if(animationIsFinished(&janim->anim)) {
// jerry_value_t cResult = moduleAnimationFireComplete(
// callInfo->this_value
// );
// if(jerry_value_is_exception(cResult)) return cResult;
// jerry_value_free(cResult);
// }
return jerry_number((double)janim->value);
}
// return jerry_number((double)janim->value);
// }
moduleBaseFunction(moduleAnimationGetValue) {
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
if(!janim) return moduleBaseThrow("Invalid Animation instance");
return jerry_number((double)janim->value);
}
// moduleBaseFunction(moduleAnimationGetValue) {
// jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
// if(!janim) return moduleBaseThrow("Invalid Animation instance");
// return jerry_number((double)janim->value);
// }
moduleBaseFunction(moduleAnimationReset) {
animation_t *anim = moduleAnimationGet(callInfo);
if(!anim) return moduleBaseThrow("Invalid Animation instance");
animationReset(anim);
// moduleBaseFunction(moduleAnimationReset) {
// animation_t *anim = moduleAnimationGet(callInfo);
// if(!anim) return moduleBaseThrow("Invalid Animation instance");
// animationReset(anim);
jerry_value_t key = jerry_string_sz("_completeFired");
jerry_value_t falseVal = jerry_boolean(false);
jerry_object_set(callInfo->this_value, key, falseVal);
jerry_value_free(key);
jerry_value_free(falseVal);
// jerry_value_t key = jerry_string_sz("_completeFired");
// jerry_value_t falseVal = jerry_boolean(false);
// jerry_object_set(callInfo->this_value, key, falseVal);
// jerry_value_free(key);
// jerry_value_free(falseVal);
return jerry_undefined();
}
// return jerry_undefined();
// }
moduleBaseFunction(moduleAnimationGetComplete) {
animation_t *anim = moduleAnimationGet(callInfo);
return anim ? jerry_boolean(animationIsFinished(anim)) : jerry_boolean(false);
}
// moduleBaseFunction(moduleAnimationGetComplete) {
// animation_t *anim = moduleAnimationGet(callInfo);
// return anim ? jerry_boolean(animationIsFinished(anim)) : jerry_boolean(false);
// }
moduleBaseFunction(moduleAnimationGetLoop) {
animation_t *anim = moduleAnimationGet(callInfo);
return anim ? jerry_boolean(animationIsLooping(anim)) : jerry_boolean(false);
}
// moduleBaseFunction(moduleAnimationGetLoop) {
// animation_t *anim = moduleAnimationGet(callInfo);
// return anim ? jerry_boolean(animationIsLooping(anim)) : jerry_boolean(false);
// }
moduleBaseFunction(moduleAnimationSetLoop) {
animation_t *anim = moduleAnimationGet(callInfo);
if(!anim) return moduleBaseThrow("Invalid Animation instance");
if(argc < 1) return moduleBaseThrow("Expected boolean");
if(jerry_value_is_true(args[0])) {
anim->flags |= ANIMATION_FLAG_LOOP_ENABLED;
} else {
anim->flags &= ~ANIMATION_FLAG_LOOP_ENABLED;
}
return jerry_undefined();
}
// moduleBaseFunction(moduleAnimationSetLoop) {
// animation_t *anim = moduleAnimationGet(callInfo);
// if(!anim) return moduleBaseThrow("Invalid Animation instance");
// if(argc < 1) return moduleBaseThrow("Expected boolean");
// if(jerry_value_is_true(args[0])) {
// anim->flags |= ANIMATION_FLAG_LOOP_ENABLED;
// } else {
// anim->flags &= ~ANIMATION_FLAG_LOOP_ENABLED;
// }
// return jerry_undefined();
// }
moduleBaseFunction(moduleAnimationGetTime) {
animation_t *anim = moduleAnimationGet(callInfo);
return anim ? jerry_number((double)anim->time) : jerry_number(0.0);
}
// moduleBaseFunction(moduleAnimationGetTime) {
// animation_t *anim = moduleAnimationGet(callInfo);
// return anim ? jerry_number((double)anim->time) : jerry_number(0.0);
// }
moduleBaseFunction(moduleAnimationGetDuration) {
animation_t *anim = moduleAnimationGet(callInfo);
return anim ? jerry_number((double)anim->duration) : jerry_number(0.0);
}
// moduleBaseFunction(moduleAnimationGetDuration) {
// animation_t *anim = moduleAnimationGet(callInfo);
// return anim ? jerry_number((double)anim->duration) : jerry_number(0.0);
// }
static void moduleAnimation(void) {
scriptProtoInit(
&MODULE_ANIMATION_PROTO,
"Animation",
sizeof(jsAnimation_t),
moduleAnimationConstructor
);
// scriptProtoInit(
// &MODULE_ANIMATION_PROTO,
// "Animation",
// sizeof(jsAnimation_t),
// moduleAnimationConstructor
// );
scriptProtoDefineFunc(
&MODULE_ANIMATION_PROTO, "update", moduleAnimationUpdate
);
scriptProtoDefineFunc(
&MODULE_ANIMATION_PROTO, "getValue", moduleAnimationGetValue
);
scriptProtoDefineFunc(
&MODULE_ANIMATION_PROTO, "reset", moduleAnimationReset
);
scriptProtoDefineProp(
&MODULE_ANIMATION_PROTO, "complete",
moduleAnimationGetComplete, NULL
);
scriptProtoDefineProp(
&MODULE_ANIMATION_PROTO, "loop",
moduleAnimationGetLoop, moduleAnimationSetLoop
);
scriptProtoDefineProp(
&MODULE_ANIMATION_PROTO, "time",
moduleAnimationGetTime, NULL
);
scriptProtoDefineProp(
&MODULE_ANIMATION_PROTO, "duration",
moduleAnimationGetDuration, NULL
);
// scriptProtoDefineFunc(
// &MODULE_ANIMATION_PROTO, "update", moduleAnimationUpdate
// );
// scriptProtoDefineFunc(
// &MODULE_ANIMATION_PROTO, "getValue", moduleAnimationGetValue
// );
// scriptProtoDefineFunc(
// &MODULE_ANIMATION_PROTO, "reset", moduleAnimationReset
// );
// scriptProtoDefineProp(
// &MODULE_ANIMATION_PROTO, "complete",
// moduleAnimationGetComplete, NULL
// );
// scriptProtoDefineProp(
// &MODULE_ANIMATION_PROTO, "loop",
// moduleAnimationGetLoop, moduleAnimationSetLoop
// );
// scriptProtoDefineProp(
// &MODULE_ANIMATION_PROTO, "time",
// moduleAnimationGetTime, NULL
// );
// scriptProtoDefineProp(
// &MODULE_ANIMATION_PROTO, "duration",
// moduleAnimationGetDuration, NULL
// );
}
+1 -1
View File
@@ -28,7 +28,7 @@ moduleBaseFunction(moduleSceneSet) {
if(argc < 1) return moduleBaseThrow("Expected at least 1 argument");
moduleBaseRequireString(0);
char_t name[ASSET_FILE_PATH_MAX];
char_t name[ASSET_FILE_NAME_MAX];
moduleBaseToString(args[0], name, sizeof(name));
if(name[0] == '\0') return moduleBaseThrow("Scene.set: name cannot be empty");
+6
View File
@@ -18,4 +18,10 @@ uint32_t mathNextPowTwo(uint32_t value) {
value |= value >> 8;
value |= value >> 16;
return value + 1;
}
float_t mathModFloat(float_t x, float_t y) {
float_t result = fmodf(x, y);
if(result < 0) result += y;
return result;
}
+11 -1
View File
@@ -50,4 +50,14 @@ uint32_t mathNextPowTwo(uint32_t value);
* @param amt The number to get the absolute value of.
* @return The absolute value of the number.
*/
#define mathAbs(amt) ((amt) < 0 ? -(amt) : (amt))
#define mathAbs(amt) ((amt) < 0 ? -(amt) : (amt))
/**
* Performs a modulus operation that always returns a non-negative result, even
* if the dividend is negative.
*
* @param x The dividend.
* @param y The divisor.
* @return The result of the modulus operation.
*/
float_t mathModFloat(float_t x, float_t y);
+11 -11
View File
@@ -14,12 +14,12 @@ errorret_t assetInitLinux(void) {
// Engine may have been provided the launch path
if(ENGINE.argc > 0) {
// Get the directory of the executable
char_t buffer[ASSET_FILE_PATH_MAX];
stringCopy(buffer, ENGINE.argv[0], ASSET_FILE_PATH_MAX);
char_t buffer[ASSET_SYSTEM_PATH_MAX];
stringCopy(buffer, ENGINE.argv[0], ASSET_SYSTEM_PATH_MAX);
size_t len = strlen(buffer);
// Normalize slashes
for(size_t i = 0; i < ASSET_FILE_PATH_MAX; i++) {
for(size_t i = 0; i < ASSET_SYSTEM_PATH_MAX; i++) {
if(buffer[i] == '\0') break;
if(buffer[i] == '\\') buffer[i] = '/';
}
@@ -38,36 +38,36 @@ errorret_t assetInitLinux(void) {
// Did we find a slash?
if(end != buffer) {
// We found the directory, set as system path
stringCopy(ASSET.platform.systemPath, buffer, ASSET_FILE_PATH_MAX);
stringCopy(ASSET.platform.systemPath, buffer, ASSET_SYSTEM_PATH_MAX);
}
} else {
// Default system path, intended to be overridden by the platform
stringCopy(ASSET.platform.systemPath, ".", ASSET_FILE_PATH_MAX);
stringCopy(ASSET.platform.systemPath, ".", ASSET_SYSTEM_PATH_MAX);
}
// Open zip file
char_t searchPath[ASSET_FILE_PATH_MAX];
char_t searchPath[ASSET_SYSTEM_PATH_MAX];
const char_t **path = ASSET_LINUX_SEARCH_PATHS;
int32_t error;
do {
char_t temp[ASSET_FILE_PATH_MAX];
char_t temp[ASSET_SYSTEM_PATH_MAX];
snprintf(
temp,
ASSET_FILE_PATH_MAX,
ASSET_SYSTEM_PATH_MAX,
*path,
ASSET_FILE_NAME
);
// Ensure combined length does not exceed ASSET_FILE_PATH_MAX
// Ensure combined length does not exceed ASSET_SYSTEM_PATH_MAX
size_t syslen = strlen(ASSET.platform.systemPath);
size_t slashlen = 1; // for '/'
size_t max_temp = ASSET_FILE_PATH_MAX - syslen - slashlen - 1;
size_t max_temp = ASSET_SYSTEM_PATH_MAX - syslen - slashlen - 1;
if(strlen(temp) > max_temp) {
temp[max_temp] = '\0';
}
snprintf(
searchPath,
ASSET_FILE_PATH_MAX,
ASSET_SYSTEM_PATH_MAX,
"%s/%s",
ASSET.platform.systemPath,
temp
+3 -1
View File
@@ -9,6 +9,8 @@
#include "error/error.h"
#include "asset/assetfile.h"
#define ASSET_SYSTEM_PATH_MAX FILENAME_MAX
static const char_t *ASSET_LINUX_SEARCH_PATHS[] = {
"%s",
"../%s",
@@ -19,7 +21,7 @@ static const char_t *ASSET_LINUX_SEARCH_PATHS[] = {
};
typedef struct {
char_t systemPath[ASSET_FILE_PATH_MAX];
char_t systemPath[ASSET_SYSTEM_PATH_MAX];
} assetlinux_t;
/**