diff --git a/scripts/components.js b/scripts/components.js index 1ab22e19..5388c006 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -94,8 +94,7 @@ var sprite = { this.sync(); this.play(); this.transform.scale = [this.image.texture.width, this.image.texture.height]; -// spritetree.remove(this); - + this._sprite.set_image(this.image); }, stop() { this.del_anim?.(); @@ -143,6 +142,7 @@ var sprite = { anchor: [0, 0], sync: function sync() { this.layer = this.gameobject.drawlayer; + this._sprite.layer = this.layer; }, pick() { return this; @@ -234,15 +234,26 @@ sprite.inputs.kp1 = function () { component.sprite = function (obj) { var sp = Object.create(sprite); + + var msp = os.make_sprite(); + sp._sprite = msp; + msp.color = Color.white; + sp.gameobject = obj; sp.transform = os.make_transform(); sp.transform.parent = obj.transform; sp.guid = prosperon.guid(); allsprites.push(sp); sp.transform.change_hook = function() { - sprite_qt.remove(sp); - sp.rect = sp.transform.torect(); - sprite_qt.insert(sp); + sprite_qt.remove(msp); + msp.rect = sp.transform.torect(); + msp.set_affine(sp.transform); + sprite_qt.insert(msp); + +/* sprite_qt.remove(sp) + sp.rect = sp.transform.torect() + sprite_qt.insert(sp) +*/ } return sp; }; diff --git a/source/HandmadeMath.c b/source/HandmadeMath.c index c95c9351..257d6e70 100644 --- a/source/HandmadeMath.c +++ b/source/HandmadeMath.c @@ -2003,3 +2003,15 @@ HMM_Quat HMM_QFromAxisAngle_LH(HMM_Vec3 Axis, float AngleOfRotation) { return HMM_QFromAxisAngle_RH(Axis, -AngleOfRotation); } + +HMM_Mat3 HMM_Mat4ToMat3(HMM_Mat4 m4) +{ + HMM_Mat3 result = { + .Elements = { + m4.Elements[0][0], m4.Elements[0][1], m4.Elements[0][2], + m4.Elements[1][0], m4.Elements[1][1], m4.Elements[1][2], + m4.Elements[2][0], m4.Elements[2][1], m4.Elements[2][2] + } + }; + return result; +} diff --git a/source/HandmadeMath.h b/source/HandmadeMath.h index 57b07105..6476fa52 100644 --- a/source/HandmadeMath.h +++ b/source/HandmadeMath.h @@ -661,6 +661,7 @@ HMM_Quat HMM_SLerp(HMM_Quat Left, float Time, HMM_Quat Right); HMM_Mat4 HMM_QToM4(HMM_Quat Left); HMM_Mat4 HMM_M4TRS(HMM_Vec3 t, HMM_Quat q, HMM_Vec3 s); HMM_Mat3 HMM_M3TRS(HMM_Vec2 t, float angle, HMM_Vec2 s); +HMM_Mat3 HMM_Mat4ToMat3(HMM_Mat4 m); HMM_Quat HMM_M4ToQ_RH(HMM_Mat4 M); HMM_Quat HMM_M4ToQ_LH(HMM_Mat4 M); diff --git a/source/jsffi.c b/source/jsffi.c index e7608253..9486b156 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -69,6 +69,7 @@ static JSAtom num_indices_atom; static JSAtom transform_atom; static JSAtom image_atom; static JSAtom layer_atom; +static JSAtom texture_atom; static JSAtom parent_atom; // GPU ATOMS @@ -1108,16 +1109,41 @@ void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \ SDL_ReleaseGPU##NAME(global_gpu, c); } \ QJSCLASS(SDL_GPU##NAME) \ -QJSCLASS(transform) +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); +} +QJSCLASSMARK(sprite) + +static JSClassID js_transform_id; +static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { + transform *t = JS_GetOpaque(val, js_transform_id); + if (!t) return; + // Mark the JSValue references stored in your struct + JS_MarkValue(rt, t->change_hook, mark_func); + JS_MarkValue(rt, t->jsparent, mark_func); + // Mark the array elements + for (int i = 0; i < arrlen(t->jschildren); i++) + JS_MarkValue(rt, t->jschildren[i], mark_func); +} + +QJSCLASSMARK(transform) QJSCLASS(font) //QJSCLASS(warp_gravity) //QJSCLASS(warp_damp) QJSCLASS(datastream) -QJSCLASS(timer) + +static JSClassID js_timer_id; +static void js_timer_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { + timer *t = JS_GetOpaque(val,js_timer_id); + if (!t) return; + JS_MarkValue(rt, t->fn, mark_func); +} +QJSCLASSMARK(timer) + QJSCLASS(skin) - -QJSCLASS(jssprite) - QJSCLASS(SDL_Window) QJSCLASS(SDL_Renderer) QJSCLASS(SDL_Camera) @@ -3959,22 +3985,57 @@ typedef struct { text_vert vert[4]; } quad; -typedef struct { - quad quad; - JSValue image; - int layer; - JSContext *js; - JSValue sprite; -} sprite; +// Comparator inline for potential compiler inlining +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->tex != b->tex) + return (a->tex < b->tex) ? -1 : 1; + + return 0; +} + +// Merge two sorted sub-ranges [left..mid] and [mid+1..right] +static void merge_sprites(sprite *arr, sprite *temp, size_t left, size_t mid, size_t right) { + size_t i = left, j = mid + 1, k = left; + + while (i <= mid && j <= right) + if (sprite_compare(&arr[i], &arr[j]) <= 0) temp[k++] = arr[i++]; + else temp[k++] = arr[j++]; + + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + + for (size_t p = left; p <= right; p++) + arr[p] = temp[p]; +} + +// Recursive mergesort +static void merge_sort_recursive(sprite *arr, sprite *temp, size_t left, size_t right) { + if (left >= right) return; + size_t mid = (left + right) / 2; + merge_sort_recursive(arr, temp, left, mid); + merge_sort_recursive(arr, temp, mid + 1, right); + merge_sprites(arr, temp, left, mid, right); +} + +// Public function to sort the array of sprites with mergesort +void better_sort_sprites(sprite *arr, size_t length) { + if (length < 2) return; + sprite *temp = malloc(length * sizeof(sprite)); + if (!temp) return; // fallback or handle error + merge_sort_recursive(arr, temp, 0, length - 1); + free(temp); +} int sort_sprite(const sprite *a, const sprite *b) { if (a->layer != b->layer) return a->layer - b->layer; - - if (a->quad.vert[0].pos.y != b->quad.vert[0].pos.y) return b->quad.vert[0].pos.y - a->quad.vert[0].pos.y; - - if (!JS_SameValue(a->js, a->image, b->image)) return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - + 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; return 0; } @@ -4001,71 +4062,27 @@ JSC_CCALL(gpu_sort_sprite, return number2js(js,0); ) -inline int sprite_in_view(HMM_Mat4 sprite, HMM_Mat4 camera) -{ - HMM_Mat4 camsprite = HMM_MulM4(camera, sprite); - int outside = 0; - for (int j = 0; j < 4; j++) { - HMM_Vec4 clip = HMM_MulM4V4_P(&camsprite, &base_quad_4[j]); - - if (clip.w <= 0.0) { - outside++; - continue; - } - - float nx = clip.x/clip.w; - float ny = clip.y/clip.w; - float nz = clip.z/clip.w; - - if (nx < -1 || nx > 1) outside++; - else if (ny < -1 || ny > 1) outside++; - else if (nz < -1 || nz > 1) outside++; - } - - return outside != 4; -} - JSC_CCALL(gpu_make_sprite_queue, size_t quads = js_arrlen(js, argv[0]); - sprite *sprites = NULL; arrsetcap(sprites, quads); for (int i = 0; i < quads; i++) { JSValue sub = JS_GetPropertyUint32(js, argv[0], i); - - rect src; - HMM_Vec4 color; - rect pr; - - JS_GETATOM(js,pr,sub,rect_atom,rect); - JS_GETATOM(js, src, sub, src_atom, rect) - JS_GETATOM(js, color, sub, color_atom, color) - - // Profile: build quad - quad sprite_quad; - sprite_quad.vert[0].pos = (HMM_Vec2){pr.x, pr.y}; - sprite_quad.vert[1].pos = (HMM_Vec2){pr.x+pr.w, pr.y}; - sprite_quad.vert[2].pos = (HMM_Vec2){pr.x, pr.y+pr.h}; - sprite_quad.vert[3].pos = (HMM_Vec2){pr.x+pr.w, pr.y+pr.h}; - - sprite_quad.vert[0].uv = (HMM_Vec2){ src.x, src.y + src.h }; - sprite_quad.vert[1].uv = (HMM_Vec2){ src.x+src.w, src.y + src.h }; - sprite_quad.vert[2].uv = (HMM_Vec2){ src.x, src.y }; - sprite_quad.vert[3].uv = (HMM_Vec2){ src.x+src.w, src.y }; - - sprite_quad.vert[0].color = color; - sprite_quad.vert[1].color = color; - sprite_quad.vert[2].color = color; - sprite_quad.vert[3].color = color; - - sprite sp; - sp.quad = sprite_quad; - sp.image = JS_GetProperty(js, sub, image_atom); - sp.js = js; - JS_GETATOM(js, sp.layer, sub, layer_atom, number) - arrput(sprites, sp); - + sprite *jsp = js2sprite(js, sub); + if (jsp) { + arrput(sprites, *jsp); + JS_DupValue(js,jsp->image); + } + else { + sprite sp = {0}; + JS_GETATOM(js,sp.affine, sub, rect_atom, rect) + JS_GETATOM(js,sp.color,sub,color_atom,color) + JS_GETATOM(js,sp.layer,sub,layer_atom,number) + JS_GETATOM(js,sp.uv,sub,src_atom,rect) + sp.image = JS_GetProperty(js,sub,image_atom); + arrput(sprites,sp); + } JS_FreeValue(js, sub); } @@ -4073,9 +4090,33 @@ JSC_CCALL(gpu_make_sprite_queue, text_vert *buffer = NULL; arrsetcap(buffer, arrlen(sprites)*4); - for (int i = 0; i < arrlen(sprites); i++) - for (int j = 0; j < 4; j++) - arrput(buffer, sprites[i].quad.vert[j]); + + for (int i = 0; i < arrlen(sprites); i++) { + rect pr = sprites[i].affine; + rect uv = sprites[i].uv; + HMM_Vec4 c = sprites[i].color; + + text_vert v[4]; + v[0].pos = (HMM_Vec2){ pr.x, pr.y }; + v[1].pos = (HMM_Vec2){ pr.x+pr.w, pr.y }; + v[2].pos = (HMM_Vec2){ pr.x, pr.y+pr.h }; + v[3].pos = (HMM_Vec2){ pr.x+pr.w, pr.y+pr.h }; + + v[0].uv = (HMM_Vec2){ uv.x, uv.y+uv.h }; + v[1].uv = (HMM_Vec2){ uv.x+uv.w, uv.y+uv.h }; + v[2].uv = (HMM_Vec2){ uv.x, uv.y }; + v[3].uv = (HMM_Vec2){ uv.x+uv.w, uv.y }; + + v[0].color = c; + v[1].color = c; + v[2].color = c; + v[3].color = c; + + arrput(buffer, v[0]); + arrput(buffer, v[1]); + arrput(buffer, v[2]); + arrput(buffer, v[3]); + } JSValue mesh = quads_to_mesh(js, buffer); @@ -4094,17 +4135,16 @@ JSC_CCALL(gpu_make_sprite_queue, JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry")); JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh)); JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2])); - JS_SetPropertyStr(js, q, "image", JS_DupValue(js, img)); + JS_SetPropertyStr(js, q, "image", img); JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index)); JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6)); JS_SetPropertyUint32(js, ret, n++, q); } first_index = i*6; count = 1; - JS_FreeValue(js, img); img = JS_DupValue(js, sprites[i].image); } else count++; - JS_FreeValue(js, sprites[i].image); + JS_FreeValue(js,sprites[i].image); } if (count > 0) { @@ -4112,7 +4152,7 @@ JSC_CCALL(gpu_make_sprite_queue, JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry")); JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh)); JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2])); - JS_SetPropertyStr(js, q, "image", JS_DupValue(js, img)); + JS_SetPropertyStr(js, q, "image", img); JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index)); JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6)); JS_SetPropertyUint32(js, ret, n++, q); @@ -4122,7 +4162,6 @@ JSC_CCALL(gpu_make_sprite_queue, JS_FreeValue(js, mesh); ) - JSC_CCALL(gpu_make_sprite_mesh, size_t quads = js_arrlen(js, argv[0]); size_t verts = quads*4; @@ -6441,6 +6480,7 @@ JSC_CCALL(os_make_font, ) JSC_CCALL(os_make_transform, return transform2js(js,make_transform())) +JSC_CCALL(os_make_sprite, return sprite2js(js,make_sprite())) JSC_SCALL(os_system, return number2js(js,system(str)); ) @@ -7094,6 +7134,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, make_cursor, 1), MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_transform, 0), + MIST_FUNC_DEF(os, make_sprite, 0), MIST_FUNC_DEF(os, make_line_prim, 5), MIST_FUNC_DEF(os, make_cylinder, 2), MIST_FUNC_DEF(os, make_cone, 2), @@ -7254,7 +7295,32 @@ static const JSCFunctionListEntry js_rtree_funcs[] = { MIST_FUNC_DEF(rtree, count, 0), }; -static const JSCFunctionListEntry js_jssprite_funcs[] = { +JSC_GETSET(sprite, layer, number) +JSC_GETSET(sprite, color, color) + +JSC_CCALL(sprite_set_affine, + sprite *sp = js2sprite(js,self); + transform *t = js2transform(js,argv[0]); +// sp->affine = HMM_Mat4ToMat3(t->gcache); + sp->affine = transform2rect(t); +) + +JSC_CCALL(sprite_set_image, + sprite *sp = js2sprite(js,self); + if (!JS_IsUndefined(sp->image)) + JS_FreeValue(js,sp->image); + sp->image = JS_DupValue(js,argv[0]); + if (JS_IsUndefined(sp->image)) return JS_UNDEFINED; + JSValue img = sp->image; + JS_GETATOM(js, sp->uv, img, rect_atom, rect) + JS_GETATOM(js, sp->tex, img, texture_atom, SDL_GPUTexture) +) + +static const JSCFunctionListEntry js_sprite_funcs[] = { + MIST_FUNC_DEF(sprite, set_affine, 1), + MIST_FUNC_DEF(sprite, set_image, 1), + CGETSET_ADD(sprite, layer), + CGETSET_ADD(sprite, color), }; #define JSSTATIC(NAME, PARENT) \ @@ -7336,7 +7402,7 @@ void ffi_load(JSContext *js) { QJSCLASSPREP_FUNCS(SDL_GPUSampler) QJSCLASSPREP_FUNCS(SDL_GPUGraphicsPipeline) QJSCLASSPREP_FUNCS(SDL_GPUComputePipeline) - QJSCLASSPREP_FUNCS(jssprite) + QJSCLASSPREP_FUNCS(sprite) // QJSCLASSPREP_FUNCS(SDL_GPUGraphicsPipeline) // QJSCLASSPREP_FUNCS(SDL_GPUSampler) // QJSCLASSPREP_FUNCS(SDL_GPUShader) @@ -7411,6 +7477,7 @@ void ffi_load(JSContext *js) { transform_atom = JS_NewAtom(js,"transform"); image_atom = JS_NewAtom(js,"image"); layer_atom = JS_NewAtom(js,"layer"); + texture_atom = JS_NewAtom(js, "texture"); cw_atom = JS_NewAtom(js,"cw"); ccw_atom = JS_NewAtom(js,"ccw"); diff --git a/source/qjs_macros.h b/source/qjs_macros.h index b4ee4fff..33db6464 100644 --- a/source/qjs_macros.h +++ b/source/qjs_macros.h @@ -101,6 +101,27 @@ JSValue TYPE##2js(JSContext *js, TYPE *n) { \ return j; }\ \ +#define QJSCLASSMARK(TYPE, ...)\ +static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\ +TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\ +TYPE##_free(rt,n);}\ +static JSClassDef js_##TYPE##_class = {\ + #TYPE,\ + .finalizer = js_##TYPE##_finalizer,\ + .gc_mark = js_##TYPE##_mark,\ +};\ +TYPE *js2##TYPE (JSContext *js, JSValue val) { \ + if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \ + return JS_GetOpaque(val,js_##TYPE##_id); \ +}\ +JSValue TYPE##2js(JSContext *js, TYPE *n) { \ + JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\ + JS_SetOpaque(j,n);\ + __VA_ARGS__ \ + return j; }\ +\ + + #define QJSGLOBALCLASS(NAME) \ JSValue NAME = JS_NewObject(js); \ JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \ diff --git a/source/sprite.c b/source/sprite.c index 4b8bb6fb..85077f35 100644 --- a/source/sprite.c +++ b/source/sprite.c @@ -1,6 +1,13 @@ #include "sprite.h" -void jssprite_free(JSRuntime *rt, jssprite *sprite) +sprite *make_sprite() { + sprite *sprite = calloc(sizeof(*sprite),1); + return sprite; +} + +void sprite_free(JSRuntime *rt, sprite *sprite) +{ + JS_FreeValueRT(rt,sprite->image); free(sprite); -} \ No newline at end of file +} diff --git a/source/sprite.h b/source/sprite.h index 8bd3dc97..7d48e930 100644 --- a/source/sprite.h +++ b/source/sprite.h @@ -3,15 +3,20 @@ #include "HandmadeMath.h" #include "script.h" +#include "render.h" -struct jssprite{ - HMM_Mat3 affine; +struct sprite{ + rect affine; JSValue image; + SDL_GPUTexture *tex; + rect uv; int layer; + HMM_Vec4 color; }; -typedef struct jssprite jssprite; +typedef struct sprite sprite; -void jssprite_free(JSRuntime *rt, jssprite *sprite); +sprite *make_sprite(); +void sprite_free(JSRuntime *rt, sprite *sprite); #endif diff --git a/source/transform.c b/source/transform.c index 7cbb3cc1..c4e5d979 100644 --- a/source/transform.c +++ b/source/transform.c @@ -16,6 +16,17 @@ transform *make_transform() return t; } +void transform_free(JSRuntime *rt, transform *t) { + JS_FreeValueRT(rt,t->change_hook); + JS_FreeValueRT(rt,t->jsparent); + for (int i = 0; i < arrlen(t->jschildren); i++) + JS_FreeValueRT(rt,t->jschildren[i]); + + arrfree(t->jschildren); + arrfree(t->children); + free(t); +} + void transform_clean(transform *t) { if (!t->dirty) return; @@ -26,14 +37,6 @@ void transform_clean(transform *t) else t->gcache = t->cache; - HMM_Mat4 m = t->gcache; - rect r = {0}; - r.x = m.Columns[3].x; - r.y = m.Columns[3].y; - r.w = m.Columns[0].x; - r.h = m.Columns[1].y; - t->rcache = r; - for (int i = 0; i < arrlen(t->children); i++) { t->children[i]->dirty = 1; transform_clean(t->children[i]); @@ -45,17 +48,6 @@ void transform_clean(transform *t) } } -void transform_free(JSRuntime *rt, transform *t) { - JS_FreeValueRT(rt,t->change_hook); - JS_FreeValueRT(rt,t->jsparent); - for (int i = 0; i < arrlen(t->jschildren); i++) - JS_FreeValueRT(rt,t->jschildren[i]); - - arrfree(t->jschildren); - arrfree(t->children); - free(t); - printf("FREED TRANSFORM!\n"); -} void transform_move(transform *t, HMM_Vec3 v) { t->pos = HMM_AddV3(t->pos, v); @@ -112,7 +104,13 @@ HMM_Mat4 transform2mat4_global(transform *t) rect transform2rect(transform *t) { - return t->rcache; + HMM_Mat4 m = t->gcache; + rect r = {0}; + r.x = m.Columns[3].x; + r.y = m.Columns[3].y; + r.w = m.Columns[0].x; + r.h = m.Columns[1].y; + return r; } void transform_apply(transform *t) diff --git a/source/transform.h b/source/transform.h index f2f8bf86..e6cb2506 100644 --- a/source/transform.h +++ b/source/transform.h @@ -11,7 +11,6 @@ typedef struct transform { HMM_Quat rotation; HMM_Mat4 cache; HMM_Mat4 gcache; - rect rcache; int dirty; struct transform *parent; JSValue jsparent;