Files
prosperon/sprite.c
2025-12-01 09:55:52 -06:00

146 lines
4.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "prosperon.h"
#include "cell.h"
#include "HandmadeMath.h"
struct sprite{
HMM_Vec2 pos; // x,y coordinates of the sprite
HMM_Vec2 center;
HMM_Vec2 skew;
HMM_Vec2 scale;
float rotation;
HMM_Mat2 affine;
JSValue image; // the JS image
int layer; // layer this sprite draws onto
HMM_Vec4 color; // color in 0-1f
};
typedef struct sprite sprite;
sprite *make_sprite(void)
{
sprite *sprite = calloc(sizeof(*sprite),1);
sprite->image = JS_NULL;
return sprite;
}
void sprite_free(JSRuntime *rt, sprite *sprite)
{
JS_FreeValueRT(rt,sprite->image);
free(sprite);
}
void sprite_apply(sprite *sp)
{
/* -------- rotation + skew → 2×2 affine matrix -------- */
float rot = sp->rotation;
HMM_Vec2 k = sp->skew;
float c = cosf(rot), s = sinf(rot);
sp->affine.Columns[0] = (HMM_Vec2){ .x = c + k.x * s,
.y = k.y * c + s };
sp->affine.Columns[1] = (HMM_Vec2){ .x = -s + k.x * c,
.y = -k.y * s + c };
}
// Sprite class definitions
static JSClassID js_sprite_id;
static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {
sprite *sp = JS_GetOpaque(val, js_sprite_id);
if (!sp) return;
JS_MarkValue(rt, sp->image, mark_func);
}
// Class definition for sprite with mark function for GC
QJSCLASSMARK(sprite,)
// SPRITE GETTER/SETTER FUNCTIONS
JSC_GETSET(sprite, pos, vec2)
JSC_GETSET(sprite, center, vec2)
JSC_GETSET(sprite, layer, number)
JSC_GETSET(sprite, color, color)
JSC_GETSET(sprite, skew, vec2)
JSC_GETSET(sprite, scale, vec2)
JSC_GETSET(sprite, rotation, number)
// SPRITE ACTION FUNCTIONS
JSC_CCALL(sprite_move,
sprite *sp = js2sprite(js,self);
HMM_Vec2 mv = js2vec2(js,argv[0]);
sp->pos.x += mv.x;
sp->pos.y += mv.y;
)
JSC_CCALL(sprite_set_affine,
sprite *sp = js2sprite(js,self);
sprite_apply(sp);
)
JSC_CCALL(sprite_set_image,
sprite *sp = js2sprite(js,self);
if (!JS_IsNull(sp->image))
JS_FreeValue(js,sp->image);
sp->image = JS_DupValue(js, argv[0]);
)
static const JSCFunctionListEntry js_sprite_funcs[] = {
MIST_FUNC_DEF(sprite, set_affine, 0),
MIST_FUNC_DEF(sprite, set_image, 1),
MIST_FUNC_DEF(sprite, move, 1),
JS_CGETSET_DEF("pos", js_sprite_get_pos, js_sprite_set_pos),
JS_CGETSET_DEF("scale", js_sprite_get_scale, js_sprite_set_scale),
JS_CGETSET_DEF("skew", js_sprite_get_skew, js_sprite_set_skew),
JS_CGETSET_DEF("layer", js_sprite_get_layer, js_sprite_set_layer),
JS_CGETSET_DEF("color", js_sprite_get_color, js_sprite_set_color),
JS_CGETSET_DEF("center", js_sprite_get_center, js_sprite_set_center),
JS_CGETSET_DEF("rotation", js_sprite_get_rotation, js_sprite_set_rotation),
};
// Constructor function for sprite
static JSValue js_sprite_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv) {
sprite *sp = make_sprite();
if (!sp) return JS_ThrowOutOfMemory(js);
// If an options object is provided, initialize the sprite with it
if (argc > 0 && JS_IsObject(argv[0])) {
JS_GETATOM(js, sp->pos, argv[0], pos, vec2)
JS_GETATOM(js, sp->center, argv[0], center, vec2)
JS_GETATOM(js, sp->skew, argv[0], skew, vec2)
JS_GETATOM(js, sp->scale, argv[0], scale, vec2)
JS_GETATOM(js, sp->rotation, argv[0], rotation, number)
JS_GETATOM(js, sp->layer, argv[0], layer, number)
JS_GETATOM(js, sp->color, argv[0], color, color)
JSValue image = JS_GetProperty(js, argv[0], JS_NewAtom(js, "image"));
if (!JS_IsNull(image)) {
sp->image = image; // Transfer ownership, no need to dup
}
}
// Default values if not provided
if (sp->scale.x == 0 && sp->scale.y == 0) {
sp->scale.x = 1;
sp->scale.y = 1;
}
if (sp->color.w == 0) sp->color.w = 1; // Default alpha to 1
sprite_apply(sp);
return sprite2js(js, sp);
}
CELL_USE_INIT(
// Register the sprite class
QJSCLASSPREP_FUNCS(sprite);
// Create the constructor function
JSValue ctor = JS_NewCFunction2(js, js_sprite_constructor, "sprite", 1, JS_CFUNC_constructor, 0);
// Set the prototype on the constructor
JSValue proto = JS_GetClassProto(js, js_sprite_id);
JS_SetConstructor(js, ctor, proto);
JS_FreeValue(js, proto);
return ctor;
)