From 444fb98125dcf6caf0e396e6abce5b926b505bf2 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 18 Dec 2023 12:45:27 +0000 Subject: [PATCH] bezier spline controls --- docs/gui.md | 8 +-- quickjs/quickjs.c | 1 + scripts/base.js | 7 +- scripts/components.js | 136 ++++++++++++++++++++------------------ scripts/debug.js | 124 +++++++++++++++++++--------------- scripts/editor.js | 29 ++++---- scripts/engine.js | 84 +++++++++++++++++++++-- scripts/entity.js | 4 +- scripts/gui.js | 6 +- scripts/input.js | 6 +- source/engine/jsffi.c | 74 +++++++++++++++------ source/engine/resources.h | 2 + 12 files changed, 309 insertions(+), 172 deletions(-) diff --git a/docs/gui.md b/docs/gui.md index 6afea5a..36aed33 100644 --- a/docs/gui.md +++ b/docs/gui.md @@ -3,16 +3,16 @@ Register functions with built-in drawing callbacks to have code execute at particular points in time. Some drawing functions take world coordinates, while others take screen coordinates. gui -Called before nk_gui. Screen space. +Called every frame. draw -Called every frame. World space. +Called every frame. debug -Called if drawing physics. World space. +Called if drawing physics. gizmo -Called usually for editor purposes. World space. +Called on an object if it is selected in the editor. # Mum diff --git a/quickjs/quickjs.c b/quickjs/quickjs.c index 9f41ec4..7a84fc9 100644 --- a/quickjs/quickjs.c +++ b/quickjs/quickjs.c @@ -110,6 +110,7 @@ /* test the GC by forcing it before each object allocation */ //#define FORCE_GC_AT_MALLOC + #ifdef CONFIG_ATOMICS #include #include diff --git a/scripts/base.js b/scripts/base.js index 8fe2263..fc4bb6c 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -1,5 +1,3 @@ -/* Removing crud I don't like */ -/* Functions that lead to bloated error prone javascript */ /* It is EMCA6 but without a lot of builtin objects and various functions. There are no: * Promises and so on (Generators, async) * WeakMaps and so on (weakset, weakref) @@ -10,7 +8,7 @@ In addition to the removal of a bunch of stuff as seen here. Access prototypes through __proto__ instead of the long-winded Object.getProtoTypeOf. */ - +/* Object.getPrototypeOf = undefined; Object.setPrototypeOf = undefined; Reflect = undefined; @@ -22,6 +20,7 @@ WeakMap = undefined; Promise = undefined; Set = undefined; WeakSet = undefined; +*/ var fmt = {}; @@ -1127,7 +1126,7 @@ Object.defineProperty(Array.prototype, 'find', { }}); Object.defineProperty(Array.prototype, 'last', { - get: function() { return this[this.length-1]; }, + value: function() { return this[this.length-1]; }, }); Object.defineProperty(Array.prototype, 'at', { diff --git a/scripts/components.js b/scripts/components.js index faff0e8..51fd8a0 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -487,7 +487,7 @@ component.polygon2d = Object.copy(collider2d, { gizmo() { this.spoints.forEach(function(x) { - Debug.point(world2screen(this.gameobject.this2world(x)), 3, Color.green); + Shape.point(world2screen(this.gameobject.this2world(x)), 3, Color.green); }, this); this.points.forEach(function(x, i) { @@ -499,7 +499,8 @@ component.polygon2d = Object.copy(collider2d, { if (!Object.hasOwn(this,'points')) this.points = deep_copy(this.__proto__.points); - var p = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points); + var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points); + var p = this.points[i]; if (p) return make_point_obj(this, p); @@ -553,6 +554,7 @@ component.edge2d = Object.copy(collider2d, { thickness:0, /* if type === -1, point to point */ type: Spline.type.catmull, + C: 1, /* when in bezier, continuity required. 0, 1 or 2. */ looped: false, angle: 3, /* maximum angle between two segments */ @@ -612,30 +614,18 @@ component.edge2d = Object.copy(collider2d, { } if (this.type === Spline.type.catmull) { - 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])); - } + if (this.looped) + spoints = Spline.catmull_loop(spoints); + else + spoints = Spline.catmull_caps(spoints); + return Spline.sample_angle(this.type, spoints,this.angle); } - var tp = []; - for (var i = 0; i < spoints.length-1; i++) - tp.push([spoints[i+1].sub(spoints[i]), spoints[i+1].sub(spoints[i])]); - - spoints = spoints.map((p,i) => p.concat(tp[i])); - spoints = spoints.flat(); - return Spline.sample_angle(this.type, spoints, this.angle); }, - boundingbox() { - return points2bb(this.points.map(x => x.scale(this.gameobject.scale))); - }, + boundingbox() { return points2bb(this.points.map(x => x.scale(this.gameobject.scale))); }, hides: ['gameobject', 'id', 'shape'], _enghook: make_edge2d, @@ -643,27 +633,50 @@ component.edge2d = Object.copy(collider2d, { /* EDITOR */ gizmo() { if (this.type === Spline.type.catmull) { - this.spoints().forEach(x => Debug.point(this.gameobject.this2world(x), 3, Color.teal)); - this.cpoints.forEach((x,i) => Debug.numbered_point(this.gameobject.this2world(x), i)); + this.spoints().forEach(x => Shape.point(this.gameobject.this2screen(x), 3, Color.teal)); + this.cpoints.forEach((x,i) => Debug.numbered_point(this.gameobject.this2screen(x), i)); } else { - for (var i = 1; i < this.cpoints.length; i+=2) { - Debug.point(this.gameobject.this2world(this.cpoints[i]), 3, Color.green); - Debug.line([this.gameobject.this2world(this.cpoints[i-1]), this.gameobject.this2world(this.cpoints[i])], Color.yellow); + for (var i = 0; i < this.cpoints.length; i += 3) + Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i]), i, Color.teal); + + for (var i = 1; i < this.cpoints.length; i+=3) { + Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i]), i, Color.green); + Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i+1]), i+1, Color.green); + Shape.line([this.gameobject.this2screen(this.cpoints[i-1]), this.gameobject.this2screen(this.cpoints[i])], Color.yellow); + Shape.line([this.gameobject.this2screen(this.cpoints[i+1]), this.gameobject.this2screen(this.cpoints[i+2])], Color.yellow); } -// for (var i = 0; i < this.cpoints.length; i+=3) -// Debug.numbered_point(this.gameobject.this2world(this.cpoints[ } - }, - finish_center(change) { - this.cpoints = this.cpoints.map(function(x) { return x.sub(change); }); - }, + finish_center(change) { this.cpoints = this.cpoints.map(function(x) { return x.sub(change); }); }, pick(pos) { - var p = Gizmos.pick_gameobject_points(pos, this.gameobject, this.cpoints); - if (p) - return make_point_obj(this, p); + var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.cpoints); + var p = this.cpoints[i]; + var that = this.gameobject; + var me = this; + if (p) { + var o = { + pos: p, + sync: me.sync.bind(me) + }; + if (Spline.bezier_is_handle(this.cpoints,i)) + o.move = function(d) { + d = that.dir_world2this(d); + p.x += d.x; + p.y += d.y; + Spline.bezier_cp_mirror(me.cpoints,i); + }; + else + o.move = function(d) { + d = that.dir_world2this(d); + p.x += d.x; + p.y += d.y; + var pp = Spline.bezier_point_handles(me.cpoints,i); + pp.forEach(ph => me.cpoints[ph] = me.cpoints[ph].add(d)); + } + return o; + } return undefined; }, @@ -741,10 +754,25 @@ bucket.inputs['C-r'].doc = "Reverse the order of the spline's points."; bucket.inputs['C-l'] = function() { this.looped = !this.looped}; bucket.inputs['C-l'].doc = "Toggle spline being looped."; -bucket.inputs['C-c'] = function() { this.type = Spline.type.catmull; }; +bucket.inputs['C-c'] = function() { + switch(this.type) { + case Spline.type.bezier: + this.cpoints = Spline.bezier2catmull(this.cpoints); + break; + } + this.type = Spline.type.catmull; +}; + bucket.inputs['C-c'].doc = "Set type of spline to catmull-rom."; -bucket.inputs['C-b'] = function() { this.type = Spline.type.bezier; }; +bucket.inputs['C-b'] = function() { + switch(this.type) { + case Spline.type.catmull: + this.cpoints = Spline.catmull2bezier(Spline.catmull_caps(this.cpoints)); + break; + } + this.type = Spline.type.bezier; +}; bucket.inputs['C-o'] = function() { this.type = -1; }; bucket.inputs['C-o'].doc = "Set spline to linear."; @@ -760,10 +788,10 @@ bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline. bucket.inputs['C-lm'] = function() { var idx = 0; - if (this.cpoints.length >= 2) { - idx = cmd(59, screen2world(Mouse.pos).sub(this.gameobject.pos), this.cpoints, 1000); - if (idx === -1) return; - } + if (this.cpoints.length >= 2) + idx = cmd(59, screen2world(Mouse.pos).sub(this.gameobject.pos), this.cpoints, 400); + + console.say("new point"); if (idx === this.cpoints.length) this.cpoints.push(this.gameobject.world2this(screen2world(Mouse.pos))); @@ -836,30 +864,10 @@ component.circle2d.impl = Object.mix({ /* ASSETS */ var Texture = { - mipmaps(path, x) { - cmd(94, path, x); - }, + mipmaps(path, x) { cmd(94, path, x); }, - sprite(path, x) { - cmd(95, path, x); - }, + sprite(path, x) { cmd(95, path, x); }, }; -var Resources = { - load(path) { - if (path in this) - return this[path]; - - var src = {}; - this[path] = src; - src.path = path; - - if (!IO.exists(`${path}.asset`)) - return this[path]; - - var data = JSON.parse(IO.slurp(`${path}.asset`)); - Object.assign(src,data); - return this[path]; - - }, -}; +Texture.mipmaps.doc = "Return true if the texture has mipmaps."; +Texture.sprite.do = "Return true if the texture is treated as a sprite."; diff --git a/scripts/debug.js b/scripts/debug.js index e748cc1..514d05e 100644 --- a/scripts/debug.js +++ b/scripts/debug.js @@ -1,13 +1,35 @@ -var Gizmos = { - pick_gameobject_points(worldpos, gameobject, points) { - var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25); - if (idx === -1) return undefined; - return points[idx]; - }, -}; - +/* All draw in screen space */ var Shape = { circle(pos, radius, color) { cmd(115, pos, radius, color); }, + + point(pos,size,color) { + color ??= Color.blue; + Shape.circle(pos,size,color); + }, + + arrow(start, end, color, capsize) { + color ??= Color.red; + capsize ??= 4; + cmd(81, start, end, color, capsize); + }, + + poly(points, color) { cmd_points(0,points,color); }, + + box(pos, wh, color) { + color ??= Color.white; + cmd(53, pos, wh, color); + }, + + line(points, color, type, thickness) { + thickness ??= 1; + type ??= 0; + color ??= Color.white; + + switch (type) { + case 0: + cmd(83, points, color, thickness); + } + }, }; var Debug = { @@ -27,38 +49,17 @@ var Debug = { cmd(47, width, span, color); }, - point(pos, size, color) { - color = color ? color : Color.blue; - Shape.circle(pos, size, color); -// cmd(51, pos, size,color); - }, - coordinate(pos, size, color) { GUI.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color); }, - - arrow(start, end, color, capsize) { - color = color ? color : Color.red; - if (!capsize) - capsize = 4; - cmd(81, start, end, color, capsize); - }, - - poly(points, color) { - cmd_points(0,points,color); - }, boundingbox(bb, color) { color ??= Color.white; cmd_points(0, bb2points(bb), color); }, - - box(pos, wh, color) { - color ??= Color.white; - cmd(53, pos, wh, color); - }, - numbered_point(pos, n) { - Debug.point(pos, 3); - GUI.text(n, pos.add([0,4]), 1); + numbered_point(pos, n, color) { + color ??= Color.white; + Shape.point(pos, 3, color); + GUI.text(n, pos.add([0,4]), 1, color); }, phys_drawing: false, @@ -75,22 +76,7 @@ var Debug = { Register.debug.register(fn,obj); }, - gameobject(go) { - - }, - - line(points, color, type, thickness) { - thickness ??= 1; - type ??= 0; - - if (!color) - color = Color.white; - - switch (type) { - case 0: - cmd(83, points, color, thickness); - } - }, + gameobject(go) { cmd(15, go.body); }, draw_bb: false, draw_gizmos: false, @@ -136,8 +122,8 @@ Debug.Options.Color = { var Gizmos = { pick_gameobject_points(worldpos, gameobject, points) { var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25); - if (idx === -1) return null; - return points[idx]; + if (idx === -1) return undefined; + return idx; }, }; @@ -146,19 +132,51 @@ var Profile = { ns(ticks) { return cmd(128, ticks); }, us(ticks) { return cmd(129, ticks); }, ms(ticks) { return cmd(130, ticks); }, + best_t(ns) { + var e = ns; + var qq = 'ns'; + if (e > 1000) { + e /= 1000; + qq = 'us'; + if (e > 1000) { + e /= 1000; + qq = 'ms'; + } + } + return { + time: e, + unit: qq + }; + }, cpu(fn, times, q) { times ??= 1; - q ??= "ns"; + q ??= "unnamed"; var start = Profile.tick_now(); for (var i = 0; i < times; i++) fn(); + var elapsed = Profile.tick_now() - start; - Log.say(`Profiled in ${Profile[q](elapsed)/times} avg ${q}.`); + var avgt = Profile.best_t(elapsed/times); + var totalt = Profile.best_t(elapsed); + + console.say(`Profile [${q}]: ${avgt.time.toFixed(3)} ${avgt.unit} average [${totalt.time.toFixed(3)} ${totalt.unit} for ${times} loops]`); }, get fps() { return sys_cmd(8); }, }; + + +Profile.test = { + barecall() { profile(0); }, + unpack_num(n) { profile(1,n); }, + unpack_array(n) { profile(2,n); }, + pack_num() { profile(3); }, + pack_string() { profile(6); }, + unpack_string(s) { profile(4,s); }, + unpack_32farr(a) { profile(5,a); }, +}; + Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`; /* These controls are available during editing, and during play of debug builds */ diff --git a/scripts/editor.js b/scripts/editor.js index 2e65371..0f9f98b 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -270,7 +270,7 @@ var editor = { if (!dif) return; if (this.snapshots.length !== 0) { - var ddif = ediff(dif, this.snapshots.last); + var ddif = ediff(dif, this.snapshots.last()); if (!ddif) return; dif = ddif; } @@ -344,7 +344,7 @@ var editor = { root = root ? root + "." : root; Object.entries(obj.objects).forEach(function(x) { var p = root + x[0]; - GUI.text(p, world2screen(x[1].worldpos()), 1, editor.color_depths[depth]); + GUI.text(p, x[1].screenpos(), 1, editor.color_depths[depth]); editor.draw_objects_names(x[1], p, depth+1); }); }, @@ -367,14 +367,14 @@ var editor = { color_depths: [], draw() { - Debug.point(world2screen(this.cursor), 2, Color.green); + Shape.point(world2screen(this.cursor), 2, Color.green); this.selectlist.forEach(x => { if ('gizmo' in x && typeof x['gizmo'] === 'function' ) x.gizmo(); }); - Debug.line(bb2points(cwh2bb([0,0],[Game.native.x,Game.native.y])).wrapped(1), Color.yellow); + Shape.line(bb2points(cwh2bb([0,0],[Game.native.x,Game.native.y])).wrapped(1), Color.yellow); /* Draw selection box */ if (this.sel_start) { @@ -389,7 +389,7 @@ var editor = { wh[1] = Math.abs(endpos[1] - this.sel_start[1]); var bb = cwh2bb(c,wh); Debug.boundingbox(bb, Color.Editor.select.alpha(0.1)); - Debug.line(bb2points(bb).wrapped(1), Color.white); + Shape.line(bb2points(bb).wrapped(1), Color.white); } Debug.coordinate([0,0]); @@ -405,9 +405,7 @@ var editor = { if (this.comp_info && this.sel_comp) GUI.text(Input.print_pawn_kbm(this.sel_comp,false), [100,700],1); - -// GUI.text(editor.edit_level.worldpos().map(x => Math.round(x)), world2screen(editor.edit_level.worldpos()), 1, Color.red); - GUI.text("+", world2screen(editor.edit_level.worldpos()), 1, Color.blue); + GUI.text("+", editor.edit_level.screenpos(), 1, Color.blue); var thiso = editor.get_this(); var clvl = thiso; @@ -454,21 +452,20 @@ var editor = { x._ed.check_dirty(); if (x._ed.dirty) sname += "*"; - GUI.text(sname, world2screen(x.worldpos()).add([0, 32]), 1, Color.editor.ur); - GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), world2screen(x.worldpos()), 1, Color.white); -// Debug.arrow(world2screen(x.worldpos()), world2screen(x.worldpos().add(x.up().scale(40))), Color.yellow, 1); + GUI.text(sname, x.screenpos().add([0, 32]), 1, Color.editor.ur); + GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white); }); Object.entries(thiso.objects).forEach(function(x) { var p = x[1]._ed.namestr(); - GUI.text(p, world2screen(x[1].worldpos().add([0,16])),1,editor.color_depths[depth]); + GUI.text(p, x[1].screenpos().add([0,16]),1,editor.color_depths[depth]); }); var mg = physics.pos_query(Mouse.worldpos); if (mg) { var p = mg.path_from(thiso); - GUI.text(p, world2screen(Mouse.worldpos),1,Color.teal); + GUI.text(p, Mouse.screenpos(),1,Color.teal); } if (this.selectlist.length === 1) { @@ -476,7 +473,7 @@ var editor = { for (var key in this.selectlist[0].components) { var selected = this.sel_comp === this.selectlist[0].components[key]; var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]"; - GUI.text(str, world2screen(this.selectlist[0].worldpos()).add([0,-16*(i++)])); + GUI.text(str, this.selectlist[0].screenpos().add([0,-16*(i++)])); } if (this.sel_comp) { @@ -486,7 +483,7 @@ var editor = { editor.edit_level.objects.forEach(function(obj) { if (!obj._ed.selectable) - GUI.image("icons/icons8-lock-16.png", world2screen(obj.worldpos())); + GUI.image("icons/icons8-lock-16.png", obj.screenpos()); }); Debug.draw_grid(1, editor.grid_size, Color.Editor.grid.alpha(0.3)); @@ -1684,7 +1681,7 @@ var objectexplorer = Object.copy(inputpanel, { } if (!this.previous.empty) - items.push(Mum.text({str:"prev: " + this.previous.last, action: this.prev_obj})); + items.push(Mum.text({str:"prev: " + this.previous.last(), action: this.prev_obj})); Object.getOwnPropertyNames(this.obj).forEach(key => { var descriptor = Object.getOwnPropertyDescriptor(this.obj, key); diff --git a/scripts/engine.js b/scripts/engine.js index 45b8943..cab97e6 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -95,12 +95,11 @@ var timer = { load("scripts/animation.js"); var Render = { - normal() { - cmd(67); - }, + normal() { cmd(67);}, + wireframe() { cmd(68); }, + + pass() { - wireframe() { - cmd(68); }, }; @@ -125,7 +124,7 @@ function world2screen(worldpos) { return Game.camera.world2view(worldpos); } var Register = { kbm_input(mode, btn, state, ...args) { if (state === 'released') { - btn = btn.split('-').last; + btn = btn.split('-').last(); } switch(mode) { @@ -338,6 +337,45 @@ Spline.sample_angle = function(type, points, angle) { return spline_cmd(0, type, points[0].length, points, angle); } +Spline.bezier2catmull = function(b) +{ + var c = []; + for (var i = 0; i < b.length; i += 3) + c.push(b[i]); + return c; +} + +Spline.catmull2bezier = function(c) +{ + var b = []; + for (var i = 1; i < c.length-2; i++) { + b.push(c[i].slice()); + b.push(c[i+1].sub(c[i-1]).scale(0.25).add(c[i])); + b.push(c[i].sub(c[i+2]).scale(0.25).add(c[i+1])); + } + b.push(c[c.length-2]); + return b; +} + +Spline.catmull_loop = function(cp) +{ + cp = cp.slice(); + cp.unshift(cp.last()); + cp.push(cp[1]); + cp.push(cp[2]); + return cp; +} + +Spline.catmull_caps = function(cp) +{ + cp = cp.slice(); + cp.unshift(cp[0].sub(cp[1]).add(cp[0])); + cp.push(cp.last().sub(cp.at(-2).add(cp.last()))); + return cp; +} + +Spline.catmull2bezier.doc = "Given a set of control points C for a camtull-rom type curve, return a set of cubic bezier points to give the same curve." + Spline.type = { catmull: 0, bezier: 1, @@ -345,6 +383,40 @@ Spline.type = { cubichermite: 3 }; +Spline.bezier_tan_partner = function(points, i) +{ + if (i%3 === 0) return undefined; + var partner_i = (i%3) === 2 ? i-1 : i+1; + return points[i]; +} + +Spline.bezier_cp_mirror = function(points, i) +{ + if (i%3 === 0) return undefined; + var partner_i = (i%3) === 2 ? i+2 : i-2; + var node_i = (i%3) === 2 ? i+1 : i-1; + if (partner_i >= points.length || node_i >= points.length) return; + points[partner_i] = points[node_i].sub(points[i]).add(points[node_i]); +} + +Spline.bezier_point_handles = function(points, i) +{ + if (!Spline.bezier_is_node(points,i)) return []; + var a = i-1; + var b = i+1; + var c = [] + if (a > 0) + c.push(a); + + if (b < points.length) + c.push(b); + + return c; +} + +Spline.bezier_is_node = function(points, i) { return i%3 === 0; } +Spline.bezier_is_handle = function(points, i) { return !Spline.bezier_is_node(points,i); } + load("scripts/components.js"); var Game = { diff --git a/scripts/entity.js b/scripts/entity.js index 78841c3..ac05b11 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -181,6 +181,7 @@ var gameobject = { set_body(2,this.body,x); this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i]))); }, + screenpos() { return world2screen(this.worldpos()); }, worldangle() { return Math.rad2deg(q_body(2,this.body))%360; }, sworldangle(x) { set_body(0,this.body,Math.deg2rad(x)); }, @@ -280,7 +281,8 @@ var gameobject = { shove(vec) { set_body(12,this.body,vec);}, shove_at(vec, at) { set_body(14,this.body,vec,at); }, world2this(pos) { return cmd(70, this.body, pos); }, - this2world(pos) { return cmd(71, this.body,pos); }, + this2world(pos) { return cmd(71, this.body, pos); }, + this2screen(pos) { return world2screen(this.this2world(pos)); }, dir_world2this(dir) { return cmd(160, this.body, dir); }, dir_this2world(dir) { return cmd(161, this.body, dir); }, diff --git a/scripts/gui.js b/scripts/gui.js index 1210a3f..461a0c0 100644 --- a/scripts/gui.js +++ b/scripts/gui.js @@ -1,3 +1,7 @@ +/* + GUI functions take screen space coordinates +*/ + var GUI = { text(str, pos, size, color, wrap, anchor, cursor) { size ??= 1; @@ -271,7 +275,7 @@ GUI.window = function(pos, wh, color) var p = pos.slice(); p.x += wh.x/2; p.y += wh.y/2; - Debug.box(p,wh,color); + Shape.box(p,wh,color); } GUI.flush = function() { cmd(141); }; diff --git a/scripts/input.js b/scripts/input.js index ad5cbfb..87c88e2 100644 --- a/scripts/input.js +++ b/scripts/input.js @@ -4,9 +4,9 @@ var Input = { }; var Mouse = { - get pos() { - return cmd(45); - }, + get pos() { return cmd(45); }, + + screenpos() { return cmd(45); }, get worldpos() { return screen2world(cmd(45)); diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 3354659..835b70d 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -69,10 +69,7 @@ JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\ (byte & 0x02 ? '1' : '0'), \ (byte & 0x01 ? '1' : '0') -void js_setprop_str(JSValue obj, const char *prop, JSValue v) -{ - JS_SetPropertyStr(js, obj, prop, v); -} +void js_setprop_str(JSValue obj, const char *prop, JSValue v) { JS_SetPropertyStr(js, obj, prop, v); } JSValue jstzone() { @@ -140,18 +137,10 @@ int js2int(JSValue v) { return i; } -JSValue int2js(int i) { - return JS_NewInt64(js, i); -} +JSValue int2js(int i) { return JS_NewInt64(js, i); } -JSValue str2js(const char *c) { - return JS_NewString(js, c); -} - -const char *js2str(JSValue v) -{ - return JS_ToCString(js, v); -} +JSValue str2js(const char *c) { return JS_NewString(js, c); } +const char *js2str(JSValue v) { return JS_ToCString(js, v); } JSValue strarr2js(const char **c) { @@ -197,7 +186,7 @@ struct glrect js2glrect(JSValue v) { return rect; } -JSValue js_arridx(JSValue v, int idx) { return js_getpropidx( v, idx); } +JSValue js_arridx(JSValue v, int idx) { return js_getpropidx(v, idx); } int js_arrlen(JSValue v) { int len; @@ -270,9 +259,23 @@ cpBitmask js2bitmask(JSValue v) { HMM_Vec2 *js2cpvec2arr(JSValue v) { HMM_Vec2 *arr = NULL; int n = js_arrlen(v); + arrsetlen(arr,n); + for (int i = 0; i < n; i++) - arrput(arr, js2vec2(js_getpropidx( v, i))); + arr[i] = js2vec2(js_getpropidx( v, i)); + + return arr; +} +HMM_Vec2 *jsfloat2vec(JSValue v) +{ + size_t s; + void *buf = JS_GetArrayBuffer(js, &s, v); + HMM_Vec2 *arr = NULL; + int n = s/2; + n /= sizeof(float); +// arrsetcap(arr,n); +// memcpy(arr,buf,s); return arr; } @@ -400,6 +403,8 @@ JSValue duk_spline_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst samples = bezier_cb_ma_v2(points, param); break; } + + arrfree(points); @@ -573,8 +578,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 15: -// music_stop(); - break; + gameobject_draw_debug(js2gameobject(argv[1])); + break; case 16: // dbg_color = js2color(argv[1]); @@ -1054,7 +1059,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 128: - YughWarn("%g",stm_ms(9737310)); ret = JS_NewFloat64(js, stm_ns(js2uint64(argv[1]))); break; @@ -1842,6 +1846,34 @@ JSValue duk_cmd_points(JSContext *js, JSValueConst this, int argc, JSValueConst return JS_UNDEFINED; } +const char *STRTEST = "TEST STRING"; + +JSValue duk_profile(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) +{ + int cmd = js2int(argv[0]); + switch(cmd) { + case 0: + break; + case 1: + js2number(argv[1]); + break; + case 2: + js2cpvec2arr(argv[1]); + break; + case 3: + return num2js(1.0); + case 4: + js2str(argv[1]); + break; + case 5: + jsfloat2vec(argv[1]); + break; + case 6: + return JS_NewStringLen(js, STRTEST, sizeof(*STRTEST)); + } + return JS_UNDEFINED; +} + #define DUK_FUNC(NAME, ARGS) JS_SetPropertyStr(js, globalThis, #NAME, JS_NewCFunction(js, duk_##NAME, #NAME, ARGS)); void ffi_load() { @@ -1880,6 +1912,8 @@ void ffi_load() { DUK_FUNC(inflate_cpv, 3) + DUK_FUNC(profile, 2) + JS_FreeValue(js,globalThis); JS_NewClassID(&js_ptr_id); diff --git a/source/engine/resources.h b/source/engine/resources.h index c712bcc..4164b56 100644 --- a/source/engine/resources.h +++ b/source/engine/resources.h @@ -2,6 +2,8 @@ #define RESOURCES_H #include +#include "stb_ds.h" +#include "string.h" extern char *DATA_PATH;