in C sprites; transform parent child hooks

This commit is contained in:
2025-01-15 15:18:58 -06:00
parent a24d4da3c2
commit 96096adbc7
9 changed files with 238 additions and 117 deletions

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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");

View File

@@ -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)); \

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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;