159 lines
4.5 KiB
C
159 lines
4.5 KiB
C
#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 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]);
|
||
)
|
||
|
||
JSC_CCALL(sprite_set,
|
||
sprite *sp = js2sprite(js,self);
|
||
if (!JS_IsObject(argv[0])) return JS_NULL;
|
||
|
||
JSValue val;
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "pos");
|
||
if (!JS_IsNull(val)) { sp->pos = js2vec2(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "center");
|
||
if (!JS_IsNull(val)) { sp->center = js2vec2(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "skew");
|
||
if (!JS_IsNull(val)) { sp->skew = js2vec2(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "scale");
|
||
if (!JS_IsNull(val)) { sp->scale = js2vec2(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "rotation");
|
||
if (!JS_IsNull(val)) { sp->rotation = js2number(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "layer");
|
||
if (!JS_IsNull(val)) { sp->layer = js2number(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "color");
|
||
if (!JS_IsNull(val)) { sp->color = js2color(js, val); JS_FreeValue(js, val); }
|
||
|
||
val = JS_GetPropertyStr(js, argv[0], "image");
|
||
if (!JS_IsNull(val) && !JS_IsNull(val)) {
|
||
if (!JS_IsNull(sp->image)) JS_FreeValue(js, sp->image);
|
||
sp->image = val; // Transfer ownership
|
||
} else {
|
||
JS_FreeValue(js, val);
|
||
}
|
||
)
|
||
|
||
|
||
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),
|
||
MIST_FUNC_DEF(sprite, set, 1),
|
||
};
|
||
|
||
// 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(
|
||
QJSCLASSPREP_FUNCS(sprite);
|
||
JSValue ctor = JS_NewCFunction2(js, js_sprite_constructor, "sprite", 1, JS_CFUNC_generic, 0);
|
||
return ctor;
|
||
) |