From 903ffbc607928b7c83dc39a23adc1d7c0872324c Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 13 Dec 2023 01:35:34 +0000 Subject: [PATCH] fix spline segfault --- scripts/components.js | 17 ++++++---- source/engine/2dphysics.c | 39 +++++++++++---------- source/engine/2dphysics.h | 7 ++-- source/engine/HandmadeMath.h | 1 + source/engine/gameobject.c | 6 ++++ source/engine/gameobject.h | 1 + source/engine/jsffi.c | 57 ++++++++++++++++--------------- source/engine/render.c | 6 ++-- source/engine/spline.c | 66 ++++++++++++++++++++++++++++++------ 9 files changed, 132 insertions(+), 68 deletions(-) diff --git a/scripts/components.js b/scripts/components.js index ab7f6da..fd066dd 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -553,7 +553,7 @@ component.edge2d = Object.copy(collider2d, { thickness:0, type: Spline.type.catmull, looped: false, - angle: 5, + angle: 3, flipx: false, flipy: false, @@ -605,11 +605,15 @@ component.edge2d = Object.copy(collider2d, { sample(n) { var spoints = this.spoints(); -// n = this.samples * this.sample_calc(); - -/* if (this.looped) - return Spline.sample(degrees, this.dimensions, Spline.type.open, spoints.wrapped(this.degrees), n);*/ - + if (this.looped) { + spoints.unshift(spoints[spoints.length-1]); + spoints.push(spoints[1]); + spoints.push(spoints[2]); + } else { + spoints.unshift(spoints[0].sub(spoints[1]).add(spoints[0])); + spoints.push(spoints[spoints.length-1].sub(spoints[spoints.length-2]).add(spoints[spoints.length-1])); + } + return Spline.sample_angle(this.type, spoints, this.angle); }, @@ -673,6 +677,7 @@ component.edge2d.impl = Object.mix({ sync() { var sensor = this.sensor; var points = this.sample(this.samples); + if (!points) return; cmd_edge2d(0,this.id,points); this.sensor = sensor; }, diff --git a/source/engine/2dphysics.c b/source/engine/2dphysics.c index 59fbe0b..6ac540c 100644 --- a/source/engine/2dphysics.c +++ b/source/engine/2dphysics.c @@ -387,7 +387,6 @@ struct phys2d_edge *Make2DEdge(gameobject *go) { new->shape.shape = NULL; new->shape.apply = NULL; new->draws = 0; - new->closed = 0; phys2d_applyedge(new); return new; @@ -405,16 +404,14 @@ void phys2d_edgedel(struct phys2d_edge *edge) { phys2d_shape_del(&edge->shape); } -void phys2d_edgeaddvert(struct phys2d_edge *edge) { - arrput(edge->points, v2zero); +void phys2d_edgeaddvert(struct phys2d_edge *edge, HMM_Vec2 v) { + arrput(edge->points, v); if (arrlen(edge->points) > 1) arrput(edge->shapes, cpSpaceAddShape(space, cpSegmentShapeNew(edge->shape.go->body, cpvzero, cpvzero, edge->thickness))); - - phys2d_applyedge(edge); } void phys2d_edge_rmvert(struct phys2d_edge *edge, int index) { - assert(arrlen(edge->points) > index && index >= 0); + if (index>arrlen(edge->points) || index < 0) return; arrdel(edge->points, index); @@ -424,41 +421,47 @@ void phys2d_edge_rmvert(struct phys2d_edge *edge, int index) { cpSpaceRemoveShape(space, edge->shapes[index]); cpShapeFree(edge->shapes[index]); arrdel(edge->shapes, index); - phys2d_applyedge(edge); return; } - if (index != arrlen(edge->points)) { + if (index != arrlen(edge->points)) cpSegmentShapeSetEndpoints(edge->shapes[index - 1], edge->points[index - 1].cp, edge->points[index].cp); - } cpSpaceRemoveShape(space, edge->shapes[index - 1]); cpShapeFree(edge->shapes[index - 1]); arrdel(edge->shapes, index - 1); - - phys2d_applyedge(edge); } void phys2d_edge_setvert(struct phys2d_edge *edge, int index, cpVect val) { assert(arrlen(edge->points) > index && index >= 0); edge->points[index].cp = val; +} + +void phys2d_edge_update_verts(struct phys2d_edge *edge, HMM_Vec2 *verts) +{ + if (arrlen(edge->points) == arrlen(verts)) { + for (int i = 0; i < arrlen(verts); i++) + phys2d_edge_setvert(edge,i,verts[i].cp); + } else { + int vertchange = arrlen(verts)-arrlen(edge->points); + phys2d_edge_clearverts(edge); + phys2d_edge_addverts(edge,verts); + } phys2d_applyedge(edge); } void phys2d_edge_clearverts(struct phys2d_edge *edge) { - for (int i = arrlen(edge->points) - 1; i >= 0; i--) { + for (int i = arrlen(edge->points) - 1; i >= 0; i--) phys2d_edge_rmvert(edge, i); - } } -void phys2d_edge_addverts(struct phys2d_edge *edge, cpVect *verts) { - for (int i = 0; i < arrlen(verts); i++) { - phys2d_edgeaddvert(edge); - phys2d_edge_setvert(edge, i, verts[i]); - } +void phys2d_edge_addverts(struct phys2d_edge *edge, HMM_Vec2 *verts) { + for (int i = 0; i < arrlen(verts); i++) + phys2d_edgeaddvert(edge, verts[i]); } +/* Calculates all true positions of verts, links them up, and so on */ void phys2d_applyedge(struct phys2d_edge *edge) { struct gameobject *go = edge->shape.go; diff --git a/source/engine/2dphysics.h b/source/engine/2dphysics.h index 03230f4..69b855e 100644 --- a/source/engine/2dphysics.h +++ b/source/engine/2dphysics.h @@ -65,7 +65,6 @@ struct phys2d_edge { HMM_Vec2 *points; /* Points defined relative to the gameobject */ float thickness; cpShape **shapes; - int closed; /* True if the first and last points should be connected */ struct phys2d_shape shape; int draws; }; @@ -94,13 +93,15 @@ struct phys2d_edge *Make2DEdge(gameobject *go); void phys2d_edgedel(struct phys2d_edge *edge); void phys2d_applyedge(struct phys2d_edge *edge); void phys2d_dbgdrawedge(struct phys2d_edge *edge); -void phys2d_edgeaddvert(struct phys2d_edge *edge); +void phys2d_edgeaddvert(struct phys2d_edge *edge, HMM_Vec2 v); void phys2d_edge_rmvert(struct phys2d_edge *edge, int index); float phys2d_edge_moi(struct phys2d_edge *edge, float m); void phys2d_edge_setvert(struct phys2d_edge *edge, int index, cpVect val); void phys2d_edge_clearverts(struct phys2d_edge *edge); -void phys2d_edge_addverts(struct phys2d_edge *edge, cpVect *verts); +void phys2d_edge_rmvert(struct phys2d_edge *edge, int index); +void phys2d_edge_update_verts(struct phys2d_edge *edge, HMM_Vec2 *verts); +void phys2d_edge_addverts(struct phys2d_edge *edge, HMM_Vec2 *verts); void phys2d_edge_set_sensor(struct phys2d_edge *edge, int sensor); void phys2d_edge_set_enabled(struct phys2d_edge *edge, int enabled); diff --git a/source/engine/HandmadeMath.h b/source/engine/HandmadeMath.h index d938e5a..82ff11d 100644 --- a/source/engine/HandmadeMath.h +++ b/source/engine/HandmadeMath.h @@ -389,6 +389,7 @@ typedef union HMM_Mat3 { typedef union HMM_Mat4 { float Elements[4][4]; HMM_Vec4 Columns[4]; + float e[4][4]; } HMM_Mat4; diff --git a/source/engine/gameobject.c b/source/engine/gameobject.c index e3b7b91..41aea2e 100644 --- a/source/engine/gameobject.c +++ b/source/engine/gameobject.c @@ -256,3 +256,9 @@ void gameobject_draw_debug(gameobject *go) { cpVect pos = cpBodyGetPosition(go->body); cpBodyEachShape(go->body, body_draw_shapes_dbg, NULL); } + +void gameobject_draw_debugs() +{ + for (int i = 0; i < arrlen(gameobjects); i++) + gameobject_draw_debug(gameobjects[i]); +} diff --git a/source/engine/gameobject.h b/source/engine/gameobject.h index f979518..4a40189 100644 --- a/source/engine/gameobject.h +++ b/source/engine/gameobject.h @@ -88,4 +88,5 @@ void gameobject_rotate(gameobject *go, float as); void gameobject_setangle(gameobject *go, float angle); void gameobject_setpos(gameobject *go, cpVect vec); void gameobject_draw_debug(gameobject *go); +void gameobject_draw_debugs(); #endif diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 046391d..f942be0 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -266,19 +266,14 @@ cpBitmask js2bitmask(JSValue v) { return mask; } -HMM_Vec2 *cpvecarr = NULL; - /* Does not need to be freed by returning; but not reentrant */ HMM_Vec2 *js2cpvec2arr(JSValue v) { - if (cpvecarr) - arrfree(cpvecarr); - + HMM_Vec2 *arr = NULL; int n = js_arrlen(v); - for (int i = 0; i < n; i++) - arrput(cpvecarr, js2vec2(js_getpropidx( v, i))); + arrput(arr, js2vec2(js_getpropidx( v, i))); - return cpvecarr; + return arr; } JSValue bitmask2js(cpBitmask mask) { @@ -398,15 +393,13 @@ JSValue duk_spline_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst HMM_Vec2 *points = js2cpvec2arr(argv[3]); float param = js2number(argv[4]); HMM_Vec2 *samples = catmull_rom_ma_v2(points, param); - + arrfree(points); + if (!samples) return JS_UNDEFINED; -// for (int i = 0; i < arrlen(samples); i++) -// YughWarn("%g,%g", samples[i].x, samples[i].y); - JSValue arr = vecarr2js(samples, arrlen(samples)); -// arrfree(samples); + arrfree(samples); return arr; } @@ -497,6 +490,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) const char *str2 = NULL; const void *d1 = NULL; const void *d2 = NULL; + const void *v1 = NULL; gameobject *ids = NULL; gameobject *go = NULL; JSValue ret = JS_UNDEFINED; @@ -759,7 +753,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 59: - ret = JS_NewInt64(js, point2segindex(js2vec2(argv[1]), js2cpvec2arr(argv[2]), js2number(argv[3]))); + v1 = js2cpvec2arr(argv[2]); + ret = JS_NewInt64(js, point2segindex(js2vec2(argv[1]), v1, js2number(argv[3]))); break; case 60: @@ -857,7 +852,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 83: - draw_edge(js2cpvec2arr(argv[1]), js_arrlen(argv[1]), js2color(argv[2]), js2number(argv[3]), 0, 0, js2color(argv[2]), 10); + v1 = js2cpvec2arr(argv[1]); + draw_edge(v1, js_arrlen(argv[1]), js2color(argv[2]), js2number(argv[3]), 0, 0, js2color(argv[2]), 10); break; case 84: @@ -869,7 +865,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 86: - ids = phys2d_query_box_points(js2vec2(argv[1]), js2vec2(argv[2]), js2cpvec2arr(argv[3]), js2int(argv[4])); + v1 = js2cpvec2arr(argv[3]); + ids = phys2d_query_box_points(js2vec2(argv[1]), js2vec2(argv[2]), v1, js2int(argv[4])); ret = gos2ref(ids); arrfree(ids); break; @@ -1354,6 +1351,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) if (d1) free(d1); if (d2) free(d2); + if (v1) arrfree(v1); + if (!JS_IsNull(ret)) { return ret; } @@ -1750,14 +1749,17 @@ JSValue duk_make_poly2d(JSContext *js, JSValueConst this, int argc, JSValueConst JSValue duk_cmd_poly2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { int cmd = js2int(argv[0]); struct phys2d_poly *poly = js2ptr(argv[1]); + HMM_Vec2 *v1 = NULL; if (!poly) return JS_UNDEFINED; switch (cmd) { case 0: - phys2d_poly_setverts(poly, js2cpvec2arr(argv[2])); + v1 = js2cpvec2arr(argv[2]); + phys2d_poly_setverts(poly, v1); break; } + if (v1) arrfree(v1); return JS_UNDEFINED; } @@ -1765,15 +1767,9 @@ JSValue duk_cmd_poly2d(JSContext *js, JSValueConst this, int argc, JSValueConst JSValue duk_make_edge2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { gameobject *go = js2gameobject(argv[0]); struct phys2d_edge *edge = Make2DEdge(go); - - int n = js_arrlen(argv[1]); - HMM_Vec2 points[n]; - - for (int i = 0; i < n; i++) { - points[i] = js2vec2(js_getpropidx(argv[1],i)); - phys2d_edgeaddvert(edge); - phys2d_edge_setvert(edge, i, points[i].cp); - } + HMM_Vec2 *points = js2cpvec2arr(argv[1]); + phys2d_edge_update_verts(edge, points); + arrfree(points); JSValue edgeval = JS_NewObject(js); js_setprop_str(edgeval, "id", ptr2js(edge)); @@ -1790,16 +1786,19 @@ JSValue duk_cmd_edge2d(JSContext *js, JSValueConst this, int argc, JSValueConst return JS_UNDEFINED; } + HMM_Vec2 *v1 = NULL; + switch (cmd) { case 0: - phys2d_edge_clearverts(edge); - phys2d_edge_addverts(edge, js2cpvec2arr(argv[2])); + v1 = js2cpvec2arr(argv[2]); + phys2d_edge_update_verts(edge,v1); break; case 1: edge->thickness = js2number(argv[2]); break; } + if (v1) arrfree(v1); return JS_UNDEFINED; } @@ -1810,7 +1809,9 @@ JSValue duk_inflate_cpv(JSContext *js, JSValueConst this, int argc, JSValueConst double d = js2number(argv[2]); HMM_Vec2 *infl = inflatepoints(points,d,n); JSValue arr = vecarr2js(infl,arrlen(infl)); + arrfree(infl); + arrfree(points); return arr; } diff --git a/source/engine/render.c b/source/engine/render.c index aaa00cb..7db67fe 100644 --- a/source/engine/render.c +++ b/source/engine/render.c @@ -518,8 +518,10 @@ void full_2d_pass(struct window *window) call_draw(); //// DEBUG - if (debugDrawPhysics) - call_debugs(); + if (debugDrawPhysics) { + gameobject_draw_debugs(); + call_debugs(); + } debug_flush(&projection); text_flush(&projection); diff --git a/source/engine/spline.c b/source/engine/spline.c index 0519de7..a3c1806 100644 --- a/source/engine/spline.c +++ b/source/engine/spline.c @@ -2,6 +2,7 @@ #include "stb_ds.h" #include "log.h" #include "transform.h" +#include "math.h" static const HMM_Mat4 cubic_hermite_m = { 2, -2, 1, 1, @@ -121,6 +122,14 @@ static const HMM_Mat4 catmull_rom_dddm = { -9*CAT_S, 18*CAT_S, -18*CAT_S, 6*CAT_S }; +/* + [t3 t2 t1 1] B [p1 + p2 that is, point 1, tangent at point 1, point 2, tan and point 2 + t1 + t2] + +*/ + HMM_Vec4 spline_CT(HMM_Mat4 *C, float t) { float t2 = t*t; @@ -135,7 +144,7 @@ HMM_Mat4 make_G(HMM_Vec2 a, HMM_Vec2 b, HMM_Vec2 c, HMM_Vec2 d) G.Columns[0].xy = a; G.Columns[1].xy = b; G.Columns[2].xy = c; - G.Columns[3].xy = d; + G.Columns[3].xy = d; return G; } @@ -145,6 +154,11 @@ HMM_Mat4 make_C(HMM_Vec2 p0, HMM_Vec2 p1, HMM_Vec2 p2, HMM_Vec2 p3, HMM_Mat4 *B) return HMM_MulM4(G, *B); } +HMM_Mat4 catmull_C(HMM_Vec2 c0, HMM_Vec2 c1, HMM_Vec2 c2, HMM_Vec2 c3) +{ + return make_C(c0,c1,c2,c3,&catmull_rom_m); +} + HMM_Vec2 cubic_spline_d(HMM_Vec2 p0, HMM_Vec2 p1, HMM_Vec2 p2, HMM_Vec2 p3, HMM_Mat4 *m, float d) { HMM_Mat4 G = make_G(p0,p1,p2,p3); @@ -193,7 +207,7 @@ HMM_Vec2 *catmull_rom_min_seg(HMM_Vec2 *a, HMM_Vec2 *b, HMM_Vec2 *c, HMM_Vec2 *d HMM_Mat4 G = make_G(p0, *b, *c, p3); HMM_Mat4 C = HMM_MulM4(G, catmull_rom_m); HMM_Vec2 *ret = NULL; - arrsetcap(ret, 100); + arrsetcap(ret, 1000); arrput(ret, *b); spline2d_min_seg(0, 1, min_seg, &C, ret); return ret; @@ -228,15 +242,32 @@ HMM_Vec##DIM *spline2d_min_angle_##DIM(float u0, float u1, float max_angle, HMM_ arrput(V##DIM##RET,b);\ }\ -SPLINE_MIN(2) +HMM_Vec2 *spline2d_min_angle_2(float u0, float u1, float max_angle, HMM_Mat4 *C, HMM_Vec2 *arr) +{ + float umid = (u0 + u1)/2; + HMM_Vec2 a = spline_CT(C, u0)._2; + HMM_Vec2 b = spline_CT(C, u1)._2; + HMM_Vec2 m = spline_CT(C, umid)._2; + if (fabs(HMM_AngleV2(m,b)) > max_angle) { + arr = spline2d_min_angle_2(u0, umid, max_angle, C, arr); + arr = spline2d_min_angle_2(umid, u1, max_angle, C, arr); + } + else + arrput(arr,b); + + return arr; +} + + +//SPLINE_MIN(2) SPLINE_MIN(3) -/* Computes non even points to give the best looking curve */ -HMM_Vec2 *catmull_rom_min_angle(HMM_Vec2 *a, HMM_Vec2 *b, HMM_Vec2 *c, HMM_Vec2 *d, float min_angle) +/* Computes non even points to give the best looking curve for a given catmull a,b,c,d */ +HMM_Vec2 *catmull_rom_min_angle(HMM_Vec2 *a, HMM_Vec2 *b, HMM_Vec2 *c, HMM_Vec2 *d, float min_angle, HMM_Vec2 *arr) { - HMM_Mat4 G = make_G(*a, *b, *c, *d); - HMM_Mat4 C = HMM_MulM4(G, catmull_rom_m); - return spline2d_min_angle_2(0,1,min_angle*M_PI/180.0,&C); + HMM_Mat4 C = catmull_C(*a,*b,*c,*d); + arr = spline2d_min_angle_2(0,1,min_angle*M_PI/180.0,&C, arr); + return arr; } #define CR_MA(DIM) \ @@ -257,9 +288,22 @@ HMM_Vec##DIM *catmull_rom_ma_v##DIM(HMM_Vec##DIM *cp, float ma) \ return arrdup(V##DIM##RET);\ }\ -CR_MA(2) -CR_MA(3) -CR_MA(4) +//CR_MA(2) + +HMM_Vec2 *catmull_rom_ma_v2(HMM_Vec2 *cp, float ma) +{ + if (arrlen(cp) < 4) return NULL; + HMM_Vec2 *ret = NULL; + int segments = arrlen(cp)-3; + arrput(ret, cp[1]); + for (int i = 1; i < arrlen(cp)-2; i++) + ret = catmull_rom_min_angle(&cp[i-1], &cp[i], &cp[i+1], &cp[i+2], ma, ret); + + return ret; +} + +//CR_MA(3) +//CR_MA(4) HMM_Vec2 catmull_rom_query(HMM_Vec2 *cp, float d, HMM_Mat4 *G) {