From a85b1873dd742476a4206a2204e8dffe28f8c8aa Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 4 May 2025 11:28:45 -0500 Subject: [PATCH] sprite rework --- .gitignore | 1 + scripts/modules/lcdsprite.js | 136 +++++++++++++++++---------- source/jsffi.c | 172 ++++++++++++++--------------------- source/prosperon.c | 6 +- source/prosperon.h | 6 +- source/qjs_enet.c | 2 +- source/sprite.c | 29 ++++-- source/sprite.h | 9 +- 8 files changed, 190 insertions(+), 171 deletions(-) diff --git a/.gitignore b/.gitignore index d4389a9b..c87415ee 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ game.zip icon.ico steam/ subprojects/*/ +build_dbg/ \ No newline at end of file diff --git a/scripts/modules/lcdsprite.js b/scripts/modules/lcdsprite.js index 418f9b19..0122d02c 100644 --- a/scripts/modules/lcdsprite.js +++ b/scripts/modules/lcdsprite.js @@ -1,65 +1,107 @@ +/* Sprite façade (JS) ↔ struct sprite (C) + * The C side exposes per-field getters/setters plus + * `set_affine(m)` which (re)calculates the 2×2 matrix from + * scale / skew / rotation. + * We mirror every exposed field and make sure the C + * matrix is refreshed whenever one of the three factors + * changes. + */ + var sprite = {} var graphics = use('graphics') -var render = use('render') -var draw2d = use('draw2d') +var render = use('render') +var draw2d = use('draw2d') -var ursprite = {} +var SPRITE = Symbol() /* raw C sprite */ +var POS = Symbol() /* cached JS copies of simple data */ +var ROT = Symbol() +var SCALE = Symbol() +var SKEW = Symbol() +var CENTER = Symbol() +var COLOR = Symbol() -ursprite.move = function move(mv) -{ - this.rect.x += mv.x - this.rect.y += mv.y +var ursprite = { + get pos() { return this[POS] }, + set pos(v) { + this[POS] = v + this[SPRITE].pos = v + }, + + get center() { return this[CENTER] }, + set center(v){ + this[CENTER] = v + this[SPRITE].center = v + }, + + get rotation() { return this[ROT] }, + set rotation(t){ + this[ROT] = t + this[SPRITE].rotation = t * 2*Math.PI /* C expects radians */ + this[SPRITE].set_affine() + }, + + get scale() { return this[SCALE] }, + set scale(v){ + this[SCALE] = v + this[SPRITE].scale = v + this[SPRITE].set_affine() + }, + + get skew() { return this[SKEW] }, + set skew(v){ + this[SKEW] = v + this[SPRITE].skew = v + this[SPRITE].set_affine() + }, + + get layer() { return this[SPRITE].layer }, + set layer(n){ this[SPRITE].layer = n }, + + get color() { return this[COLOR] }, + set color(v){ + this[COLOR] = v + this[SPRITE].color = v + }, + + move(mv){ + this.pos = {x:this.pos.x+mv.x, y:this.pos.y+mv.y} + }, + moveto(p){ + this.pos = p + } } -ursprite.moveto = function moveto(pos) -{ - this.rect.x = pos.x - this.rect.y = pos.y -} +var _sprites = [] -ursprite.draw = function draw() -{ - draw2d.image(this.image, this.rect, 0, this.anchor) -} +sprite.create = function(image, pos, anchor=[0,0], layer=0, props={}) { + var sp = Object.create(ursprite) + var raw = graphics.make_sprite() -var sprites = [] + sp[SPRITE] = raw -sprite.create = function(image, pos, anchor = [0,0], layer = 0, props = {}) -{ - var sp = Object.create(ursprite) + sp.pos = pos + sp.rotation = 0 + sp.scale = [1,1] + sp.skew = [0,0] + sp.center = anchor + sp.color = [1,1,1,1] - var image = graphics.texture(image) - sp.image = image - sp.rect = {x:pos.x, y:pos.y, width: image.texture.width, height: image.texture.height} - sp.layer = layer - sp.props = props - sp.anchor = anchor + var tex = graphics.texture(image) + raw.set_image(tex) + sp.image = tex - sprites.push(sp) + sp.layer = layer + sp.props = props + sp.anchor = anchor + _sprites.push(sp) return sp } -sprite.forEach = function(fn) -{ - for (var sp of sprites) - fn(sp) -} - -sprite.values = function() -{ - return sprites.slice() -} - -sprite.geometry = function() -{ - return graphics.make_sprite_mesh(sprites) -} - -sprite.queue = function() -{ - return graphics.make_sprite_queue(sprites) -} +sprite.forEach = fn => { for (let s of _sprites) fn(s) } +sprite.values = () => _sprites.slice() +sprite.geometry= () => graphics.make_sprite_mesh(_sprites) +sprite.queue = () => graphics.make_sprite_queue(_sprites) return sprite diff --git a/source/jsffi.c b/source/jsffi.c index 924d7ed1..9cd0858f 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -3034,8 +3034,6 @@ JSC_CCALL(renderer_coords, JSC_CCALL(renderer_camera, renderer_ctx *ctx = js2renderer_ctx(js,self); - JSValue camera = argv[0]; - ctx->cam = camera_globals(js, argv[0]); ) @@ -3115,25 +3113,15 @@ JSC_CCALL(renderer_make_sprite_mesh, JS_SetProperty(js, ret, count, number2js(js, count)); ) -JSC_CCALL(renderer_texture_9grid, - renderer_ctx *ctx = js2renderer_ctx(js, self); - SDL_Renderer *r = ctx->sdl; - - SDL_Texture *tex = js2SDL_Texture(js, argv[0]); - - rect src = js2rect(js, argv[1]); - lrtb extents = js2lrtb(js, argv[2]); - float scale = 1; - - rect dst = js2rect(js,argv[3]); - - dst = renderer_worldrect_to_screen(ctx, dst); - - if (!SDL_RenderTexture9Grid(r, tex, &src, - extents.l, extents.r, extents.t, extents.b, - scale, &dst)) - return JS_ThrowReferenceError(js, "SDL_RenderTexture9Grid: %s", SDL_GetError()); -) +static sprite js_getsprite(JSContext *js, JSValue sp) +{ + sprite *s = js2sprite(js, sp); + if (s) + return *s; + + sprite pp = {0}; + return pp; +} JSC_CCALL(renderer_texture, renderer_ctx *ctx = js2renderer_ctx(js, self); @@ -3156,24 +3144,26 @@ JSC_CCALL(renderer_texture, JSC_CCALL(renderer_sprite, renderer_ctx *ctx = js2renderer_ctx(js, self); - sprite *sp = js2sprite(js,argv[0]); - + sprite sp = js_getsprite(js, argv[0]); SDL_Texture *tex; - JS_GETATOM(js, tex, sp->image, texture, SDL_Texture); + JS_GETATOM(js, tex, sp.image, texture, SDL_Texture); -// rect dst = renderer_worldrect_to_screen(ctx, sp->affine); - rect dst = sp->affine; - rect uv = {x:0,y:0,w:48,h:24}; - SDL_RenderTexture(ctx->sdl, tex, &uv, &dst); -) + float w = sp.uv.w, h = sp.uv.h; -JSC_CCALL(renderer_slice9, - renderer_ctx *ctx = js2renderer_ctx(js, self); - SDL_Texture *tex = js2SDL_Texture(js,argv[0]); - rect src = js2rect(js, argv[1]); - lrtb exts = js2lrtb(js, argv[2]); - rect dst = renderer_worldrect_to_screen(ctx, js2rect(js, argv[3])); - SDL_RenderTexture9Grid(ctx->sdl, tex, &src, exts.l, exts.r, exts.t, exts.b, 0.0f, &dst); + HMM_Vec2 tl_local = { -sp.center.X, -sp.center.Y }; + HMM_Vec2 tr_local = { -sp.center.X + w, -sp.center.Y }; + HMM_Vec2 bl_local = { -sp.center.X, -sp.center.Y + h }; + + HMM_Vec2 world_tl = HMM_AddV2(sp.pos, HMM_MulM2V2(sp.affine, tl_local)); + HMM_Vec2 world_tr = HMM_AddV2(sp.pos, HMM_MulM2V2(sp.affine, tr_local)); + HMM_Vec2 world_bl = HMM_AddV2(sp.pos, HMM_MulM2V2(sp.affine, bl_local)); + + SDL_FPoint origin = renderer_world_to_screen(ctx, world_tl); + SDL_FPoint right = renderer_world_to_screen(ctx, world_tr); + SDL_FPoint down = renderer_world_to_screen(ctx, world_bl); + + if (!SDL_RenderTextureAffine(ctx->sdl, tex, &sp.uv, &origin, &right, &down)) + return JS_ThrowInternalError(js, "Render sprite error: %s", SDL_GetError()); ) static const JSCFunctionListEntry js_renderer_ctx_funcs[] = { @@ -3189,7 +3179,6 @@ static const JSCFunctionListEntry js_renderer_ctx_funcs[] = { MIST_FUNC_DEF(renderer, sprite, 1), MIST_FUNC_DEF(renderer, load_texture, 1), MIST_FUNC_DEF(renderer, get_image, 1), - MIST_FUNC_DEF(renderer, slice9, 4), MIST_FUNC_DEF(renderer, scale, 1), MIST_FUNC_DEF(renderer, logical_size,1), @@ -3263,11 +3252,11 @@ static SDL_AudioSpec js2audiospec(JSContext *js, JSValue obj) JS_FreeValue(js, v); v = JS_GetPropertyStr(js, obj, "channels"); - if (!JS_IsUndefined(v)) JS_ToUint32(js, &spec.channels, v); + if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.channels, v); JS_FreeValue(js, v); v = JS_GetPropertyStr(js, obj, "samplerate"); - if (!JS_IsUndefined(v)) JS_ToUint32(js, &spec.freq, v); + if (!JS_IsUndefined(v)) JS_ToInt32(js, &spec.freq, v); JS_FreeValue(js, v); return spec; @@ -3294,7 +3283,7 @@ JSC_CCALL(sdl_audio_devices, ) JSC_CCALL(sdl_audio_open_stream, - char *type = JS_IsString(argv[0]) ? JS_ToCString(js, argv[0]) : NULL; + const char *type = JS_IsString(argv[0]) ? JS_ToCString(js, argv[0]) : NULL; SDL_AudioDeviceID devid = !strcmp(type, "capture") ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; if (type) @@ -4042,11 +4031,11 @@ typedef struct { static inline int sprite_compare(const sprite *a, const sprite *b) { if (a->layer != b->layer) return a->layer - b->layer; - if (a->affine.y != b->affine.y) - return (b->affine.y - a->affine.y); + if (a->pos.Y != b->pos.Y) + return (b->pos.Y - a->pos.Y); - if (a->tex != b->tex) - return (a->tex < b->tex) ? -1 : 1; + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; return 0; } @@ -4087,28 +4076,35 @@ void better_sort_sprites(sprite *arr, size_t length) { int sort_sprite_backtofront(const sprite *a, const sprite *b) { if (a->layer != b->layer) return a->layer - b->layer; - if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; return 0; } int sort_sprite_fronttoback(const sprite *a, const sprite *b) { if (a->layer != b->layer) return b->layer - a->layer; - if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; return 0; } int sort_sprite_texture(const sprite *a, const sprite *b) { - if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; return 0; } int sort_sprite(const sprite *a, const sprite *b) { if (a->layer != b->layer) return a->layer - b->layer; - if (a->affine.y != b->affine.y) return b->affine.y - a->affine.y; - if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + + if (a->pos.Y != b->pos.Y) + return (b->pos.Y - a->pos.Y); + + if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) + return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; return 0; } @@ -4139,7 +4135,6 @@ JSC_CCALL(gpu_make_sprite_queue, sprite *sprites = NULL; size_t quads = 0; int needfree = 1; - int sort = js2number(js,argv[3]); if (JS_IsArrayBuffer(js, argv[0])) { // test for fastest @@ -4162,26 +4157,26 @@ JSC_CCALL(gpu_make_sprite_queue, } else { sprite sp = {0}; - JS_GETATOM(js,sp.affine, sub, rect, rect) + JS_GETATOM(js, sp.pos, sub, pos, vec2) + JS_GETATOM(js, sp.center, sub, center, vec2) + JS_GETATOM(js, sp.skew, sub, skew, vec2) + JS_GETATOM(js, sp.scale, sub, scale, vec2) JS_GETATOM(js,sp.color,sub,color,color) JS_GETATOM(js,sp.layer,sub,layer,number) JS_GETATOM(js,sp.uv,sub,src,rect) sp.image = JS_GetProperty(js,sub,image); - - JS_GETATOM(js,sp.tex,sp.image,texture,SDL_GPUTexture) arrput(sprites,sp); } JS_FreeValue(js, sub); } } - if (sort) qsort(sprites, quads, sizeof(sprite), sort_sprite); -// else qsort(sprites, quads, sizeof(sprite), sort_sprite_texture); + qsort(sprites, quads, sizeof(sprite), sort_sprite); struct quad_buffers buffers = quad_buffers_new(quads*4); for (int i = 0; i < quads; i++) { - rect pr = sprites[i].affine; + rect pr = {0}; rect uv = sprites[i].uv; HMM_Vec4 c = sprites[i].color; @@ -6826,36 +6821,6 @@ JSC_CCALL(os_make_rtree, return rtree2js(js,tree); ) -JSC_CCALL(os_rects_to_sprites, - ret = JS_NewArray(js); - - JSValue image = argv[0]; - JSValue jstex = JS_GetProperty(js,image,texture); - double w, h = 0; - JS_GETATOM(js,w,jstex,width,number) - JS_GETATOM(js,h,jstex,height,number) - SDL_GPUTexture *tex = js2SDL_GPUTexture(js,jstex); - JS_FreeValue(js,jstex); - JSValue rects = argv[1]; - rect uv; - JS_GETATOM(js,uv,image,rect,rect) - - ret = JS_NewArray(js); - int n = js_arrlen(js,rects); - for (int i = 0; i < n; i++) { - JSValue sub = JS_GetPropertyUint32(js,rects,i); - sprite *s = make_sprite(); - s->affine = js2rect(js,sub); - s->affine.w = w; - s->affine.h = h; - s->image = JS_DupValue(js,image); - s->tex = tex; - s->uv = uv; - JS_SetPropertyUint32(js,ret,i,sprite2js(js,s)); - JS_FreeValue(js,sub); - } -) - JSC_CCALL(os_on, prosperon_rt *rt = JS_GetContextOpaque(js); JS_FreeValue(js, rt->on_exception); @@ -6995,7 +6960,7 @@ JSC_CCALL(os_createactor, for (int i = 0; i < margc; i++) { JSValue val = JS_GetPropertyUint32(js, argv[0], i); - char *cstr = JS_ToCString(js,val); + const char *cstr = JS_ToCString(js,val); margv[i] = strdup(cstr); JS_FreeCString(js,cstr); JS_FreeValue(js,val); @@ -7018,7 +6983,7 @@ JSC_CCALL(os_mailbox_push, void *data = value2wota(js, argv[1], JS_UNDEFINED); - char *err = send_message(id, data); + const char *err = send_message(id, data); if (err) { free(data); return JS_ThrowInternalError(js, "Could not send message: %s", err); @@ -7027,8 +6992,8 @@ JSC_CCALL(os_mailbox_push, JSC_CCALL(os_register_actor, prosperon_rt *rt = JS_GetContextOpaque(js); - char *id = JS_ToCString(js, argv[0]); - char *err = register_actor(id, rt, JS_ToBool(js, argv[2])); + const char *id = JS_ToCString(js, argv[0]); + const char *err = register_actor(id, rt, JS_ToBool(js, argv[2])); if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err); rt->message_handle = JS_DupValue(js, argv[1]); rt->context = js; @@ -7184,7 +7149,6 @@ static const JSCFunctionListEntry js_graphics_funcs[] = { MIST_FUNC_DEF(os, make_gif, 1), MIST_FUNC_DEF(os, make_aseprite, 1), MIST_FUNC_DEF(os, cull_sprites, 2), - MIST_FUNC_DEF(os, rects_to_sprites,2), MIST_FUNC_DEF(os, make_surface, 1), MIST_FUNC_DEF(os, make_cursor, 1), MIST_FUNC_DEF(os, make_font, 2), @@ -7390,19 +7354,17 @@ static const JSCFunctionListEntry js_rtree_funcs[] = { MIST_FUNC_DEF(rtree,values,0), }; +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) JSC_CCALL(sprite_set_affine, sprite *sp = js2sprite(js,self); - transform *t = js2transform(js,argv[0]); - if (t) - sp->affine = transform2rect(t); -) - -JSC_CCALL(sprite_set_rect, - sprite *sp = js2sprite(js,self); - sp->affine = js2rect(js,argv[0]); + sprite_apply(sp); ) JSC_CCALL(sprite_set_image, @@ -7413,13 +7375,15 @@ JSC_CCALL(sprite_set_image, if (JS_IsUndefined(sp->image)) return JS_UNDEFINED; JSValue img = sp->image; JS_GETATOM(js, sp->uv, img, rect, rect) - JS_GETATOM(js, sp->tex, img, texture, SDL_GPUTexture); ) static const JSCFunctionListEntry js_sprite_funcs[] = { - MIST_FUNC_DEF(sprite, set_affine, 1), - MIST_FUNC_DEF(sprite, set_rect, 1), + MIST_FUNC_DEF(sprite, set_affine, 0), MIST_FUNC_DEF(sprite, set_image, 1), + CGETSET_ADD(sprite, skew), + CGETSET_ADD(sprite, rotation), + CGETSET_ADD(sprite, pos), + CGETSET_ADD(sprite, center), CGETSET_ADD(sprite, layer), CGETSET_ADD(sprite, color), }; @@ -7563,8 +7527,8 @@ void ffi_load(JSContext *js) JS_SetPropertyUint32(js,args, i, JS_NewString(js,rt->cmd.argv[i])); JS_SetPropertyStr(js,prosp,"argv", args); - JS_SetPropertyStr(js,prosp, "version", JS_NewString(js,PROSPERON_VERSION)); - JS_SetPropertyStr(js,prosp,"revision",JS_NewString(js,PROSPERON_COMMIT)); + //JS_SetPropertyStr(js,prosp, "version", JS_NewString(js,PROSPERON_VERSION)); + //JS_SetPropertyStr(js,prosp,"revision",JS_NewString(js,PROSPERON_COMMIT)); JS_SetPropertyStr(js,prosp,"engine_start", JS_NewCFunction(js,js_os_engine_start, "engine_start", 1)); JS_FreeValue(js,globalThis); diff --git a/source/prosperon.c b/source/prosperon.c index 5c2efe3a..58d2feb2 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -238,7 +238,7 @@ prosperon_rt *get_actor(char *id) return actor; } -char *register_actor(char *id, prosperon_rt *actor, int mainthread) +const char *register_actor(const char *id, prosperon_rt *actor, int mainthread) { if (shgeti(actors, id) != -1) return "Actor with given ID already exists."; actor->main_thread_only = mainthread; @@ -249,7 +249,7 @@ char *register_actor(char *id, prosperon_rt *actor, int mainthread) return NULL; } -char *send_message(char *id, void *msg) +const char *send_message(const char *id, void *msg) { prosperon_rt *target = get_actor(id); if (!target) return "Could not get actor from id."; @@ -1425,7 +1425,7 @@ int main(int argc, char **argv) return 0; } -int actor_exists(char *id) +int actor_exists(const char *id) { int idx = shgeti(actors,id); if (idx == -1) diff --git a/source/prosperon.h b/source/prosperon.h index 0102ac19..e33990e9 100644 --- a/source/prosperon.h +++ b/source/prosperon.h @@ -71,9 +71,9 @@ extern SDL_ThreadID main_thread; extern SDL_TLSID prosperon_id; prosperon_rt *create_actor(int argc, char **argv); -char *register_actor(char *id, prosperon_rt *actor, int mainthread); +const char *register_actor(const char *id, prosperon_rt *actor, int mainthread); void actor_free(prosperon_rt *actor); -char *send_message(char *id, void *msg); +const char *send_message(const char *id, void *msg); Uint32 actor_timer_cb(prosperon_rt *actor, SDL_TimerID id, Uint32 interval); JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv); JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv); @@ -81,7 +81,7 @@ void script_startup(prosperon_rt *rt); void script_evalf(JSContext *js, const char *format, ...); JSValue script_eval(JSContext *js, const char *file, const char *script); int uncaught_exception(JSContext *js, JSValue v); -int actor_exists(char *id); +int actor_exists(const char *id); void set_actor_state(prosperon_rt *actor); int prosperon_mount_core(void); diff --git a/source/qjs_enet.c b/source/qjs_enet.c index c0e920e4..484da79a 100644 --- a/source/qjs_enet.c +++ b/source/qjs_enet.c @@ -371,7 +371,7 @@ static JSClassDef enet_peer_class = { JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) { // TODO: implement - char *hostname = JS_ToCString(js, argv[0]); + const char *hostname = JS_ToCString(js, argv[0]); JS_FreeCString(js, hostname); return JS_UNDEFINED; } diff --git a/source/sprite.c b/source/sprite.c index 92dcbece..808b3523 100644 --- a/source/sprite.c +++ b/source/sprite.c @@ -1,17 +1,8 @@ #include "sprite.h" -static sprite model = { - .affine = {.x = 0, .y = 0, .w = 0, .h = 0}, - .tex = NULL, - .uv = {.x = 0, .y = 0, .w = 1, .h = 1}, - .layer = 0, - .color = {1, 1, 1, 1} -}; - sprite *make_sprite(void) { - sprite *sprite = malloc(sizeof(*sprite)); - *sprite = model; + sprite *sprite = calloc(sizeof(*sprite),1); sprite->image = JS_UNDEFINED; return sprite; } @@ -21,3 +12,21 @@ void sprite_free(JSRuntime *rt, sprite *sprite) JS_FreeValueRT(rt,sprite->image); free(sprite); } + +void sprite_apply(sprite *sp) +{ + float rot = sp->rotation; + HMM_Vec2 k = sp->skew; + HMM_Vec2 s = sp->scale; + + float c = cosf(rot), si = sinf(rot); + + sp->affine.Columns[0] = (HMM_Vec2){ + .x = (c + k.x * si) * s.x, + .y = (k.y * c + si) * s.x + }; + sp->affine.Columns[1] = (HMM_Vec2){ + .x = (-si + k.x * c) * s.y, + .y = (-k.y * si + c) * s.y + }; +} diff --git a/source/sprite.h b/source/sprite.h index 7e29b2f5..31d1f8e7 100644 --- a/source/sprite.h +++ b/source/sprite.h @@ -7,10 +7,12 @@ struct sprite{ HMM_Vec2 pos; // x,y coordinates of the sprite - HMM_Mat2 affine_m; // defines all deformation - rect affine; // + HMM_Vec2 center; + HMM_Vec2 skew; + HMM_Vec2 scale; + float rotation; + HMM_Mat2 affine; JSValue image; // the actual texture resource - perhaps SDL_GPUTexture, or SDL_Texture, or something else ... - SDL_GPUTexture *tex; rect uv; // pixel coordinates of the sprite on its image int layer; // layer this sprite draws onto HMM_Vec4 color; // color in 0-1f @@ -20,5 +22,6 @@ typedef struct sprite sprite; sprite *make_sprite(void); void sprite_free(JSRuntime *rt, sprite *sprite); +void sprite_apply(sprite *sprite); #endif