diff --git a/Makefile b/Makefile index 11f4a5f7..cc660cef 100755 --- a/Makefile +++ b/Makefile @@ -200,6 +200,9 @@ endif all: $(NAME) cp -f $(NAME) $(APP)$(EXT) +$(APP): $(NAME) + cp -f $(NAME) $(APP) + $(NAME): $(OBJS) $(DEPS) @echo Linking $(NAME) $(CROSS)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@ @@ -282,6 +285,9 @@ clean: docs: doc/prosperon.org make -C doc mv doc/html . + +api: $(APP) + ./prosperon run 'for (var i in globalThis) say(i)' | xargs -I {} ./prosperon api {} > docs/api/{}.md TAGINC != find . -name "*.[chj]" tags: $(TAGINC) diff --git a/docs/api/spline.md b/docs/api/spline.md deleted file mode 100644 index 36ccaa47..00000000 --- a/docs/api/spline.md +++ /dev/null @@ -1,10 +0,0 @@ -# spline -#### catmull() - - - -#### bezier() - - - - diff --git a/docs/api/vector.md b/docs/api/vector.md deleted file mode 100644 index 07fe45fd..00000000 --- a/docs/api/vector.md +++ /dev/null @@ -1,14 +0,0 @@ -# vector -#### dot() - - - -#### project() - - - -#### inflate() - - - - diff --git a/scripts/engine.js b/scripts/engine.js index 129f0eab..cd21e97c 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -241,24 +241,9 @@ function use(file, env = {}, script) { use.cache = {}; global.check_registers = function (obj) { - if (typeof obj.update === "function") - obj.timers.push(Register.update.register(obj.update.bind(obj))); - - if (typeof obj.physupdate === "function") - obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj))); - - if (typeof obj.draw === "function") - obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj)); - - if (typeof obj.debug === "function") - obj.timers.push(Register.debug.register(obj.debug.bind(obj))); - - if (typeof obj.gui === "function") - obj.timers.push(Register.gui.register(obj.gui.bind(obj))); - - if (typeof obj.screengui === "function") - obj.timers.push(Register.screengui.register(obj.screengui.bind(obj))); - + for (var reg in Register.registries) + if (typeof obj[reg] === 'function') + obj.timers.push(Register.registries[reg].register(obj[reg].bind(obj))); for (var k in obj) { if (!k.startswith("on_")) continue; var signal = k.fromfirst("on_"); @@ -363,11 +348,7 @@ function process() { } var st = profile.now(); prosperon.window_render(window.size); - prosperon.draw(); - prosperon.debug(); - prosperon.gui(); - prosperon.screengui(); - prosperon.hookend?.(); + prosperon.render(); profile.addreport(profcache, "render frame", st); frames.push(profile.secs(profile.now() - startframe)); if (frames.length > 20) frames.shift(); @@ -558,7 +539,7 @@ which returns a function that, when invoked, cancels the registry. var Register = { registries: [], - add_cb(name) { + add_cb(name, e_event = false) { var n = {}; var fns = []; @@ -579,19 +560,19 @@ var Register = { }; Register[name] = n; - Register.registries.push(n); + Register.registries[name] = n; return n; }, }; -Register.add_cb("appupdate"); -Register.add_cb("update").doc = "Called once per frame."; -Register.add_cb("physupdate"); -Register.add_cb("gui"); -Register.add_cb("debug"); -Register.add_cb("draw"); -Register.add_cb("screengui"); +Register.add_cb("appupdate", true); +Register.add_cb("update", true).doc = "Called once per frame."; +Register.add_cb("physupdate", true); +Register.add_cb("gui", true); +Register.add_cb("hud", true); +Register.add_cb("debug", true); +Register.add_cb("draw", true); var Event = { events: {}, @@ -643,8 +624,7 @@ function world_start() { global.mixin("scripts/physics"); global.mixin("scripts/widget"); - -globalThis.mum = app.spawn("scripts/mum"); +global.mixin("scripts/mum"); window.title = `Prosperon v${prosperon.version}`; window.size = [500, 500]; diff --git a/scripts/gui.js b/scripts/gui.js index 7ebe1046..e41b5bb0 100644 --- a/scripts/gui.js +++ b/scripts/gui.js @@ -164,16 +164,6 @@ Mum.button = Mum.text._int.extend({ action() { console.warn("Button has no action."); }, }); -var mumcam = {}; -mumcam.transform = os.make_transform(); -mumcam.ortho = true; -mumcam.near = 0; -mumcam.far = 1000; -mumcam.transform.pos = [100,100,-100]; -mumcam.app = true; - -var textssbo = render.text_ssbo(); - Mum.window = Mum.extend({ start() { this.wh = [this.width, this.height]; @@ -190,10 +180,7 @@ Mum.window = Mum.extend({ if (item.hide) return; item.draw(pos.slice(),this); }, this); - render.set_camera(mumcam); - render.setpipeline(render.textshader.pipe); - render.shader_apply_material(render.textshader); - var bind = render.sg_bind(render.textshader, shape.quad, {text:render.font.texture}, textssbo); + bind.inst = render.flushtext(); render.spdraw(bind); gui.scissor_win(); @@ -243,12 +230,13 @@ Mum.column = Mum.extend({ }, }); +/* Mum.debug_colors = { bounds: Color.red.slice(), margin: Color.blue.slice(), padding: Color.green.slice() -}; +};*/ -Object.values(Mum.debug_colors).forEach(function(v) { v.a = 100; }); +//Object.values(Mum.debug_colors).forEach(function(v) { v.a = 100; }); -return { Mum }; +//return { Mum }; diff --git a/scripts/mum.js b/scripts/mum.js index 4442773d..5ad3f775 100644 --- a/scripts/mum.js +++ b/scripts/mum.js @@ -1,21 +1,107 @@ +globalThis.mum = {}; var panel; -self.screengui = function() -{ - if (panel) panel.gui(); +mum.base = { + padding:[0,0], /* Each element inset with this padding on all sides */ + offset:[0,0], + pos: [0,0], + font: "fonts/c64.ttf", + selectable: false, + selected: false, + font_size: 16, + text_align: "left", /* left, center, right */ + scale: 1, + angle: 0, + anchor: [0,1], + background_image: null, + hovered: {}, + text_shadow: { + pos: [0,0], + color: Color.white, + }, + text_outline: 1, /* outline in pixels */ + color: Color.white, + margin: [0,0], /* Distance between elements for things like columns */ + width: null, + height: null, + max_width: Infinity, + max_height: Infinity, + image_repeat: false, + image_repeat_offset: [0,0], + debug: false, /* set to true to draw debug boxes */ + hide: false, } -self.prompt = function(msg = "prompt", value = "", list = [], cb = function() {}) +var post = function() {}; +var posts = []; + +var context = mum.base; +var contexts = []; + +var cursor = [0,0]; + +var end = function() { - console.info(`creating popup`); - panel = Object.create(listpanel); - panel.title = msg; - panel.value = value; - panel.allassets = list; - panel.action = function() { - cb(panel.value); - panel = undefined; - } - panel.start(); - player[0].control(panel); + post(); + context = contexts.pop(); + if (!context) context = mum.base; +} + +var listpost = function() +{ + var height = 0; + if (context.height) height += context.height; + else height += (context.bb.t - context.bb.b); + cursor.y -= height; + cursor.y -= context.padding.y; +} + +var pre = function(data) +{ + if (data.hide || context.hide) return true; + data.__proto__ = context; + contexts.push(context); + context = data; +} + +mum.list = function(fn, data = {}) +{ + if (pre(data)) return; + + cursor = context.pos; + cursor = cursor.add(context.offset); + posts.push(post); + post = listpost; + + fn(); + post = posts.pop(); + end(); +} + +mum.image = function(path, data = {}) +{ + if (pre(data)) return; + + var tex = game.texture(path); + context.bb = render.image(tex, cursor, context.size); + + end(); +} + +mum.button = function(str, data = {padding:[4,4]}) +{ + if (pre(data)) return; + var bb = render.text(str, cursor.add(context.padding), context.size, context.color); + render.rectangle([bb.l-context.padding.x, bb.b-context.padding.y], [bb.r+context.padding.y, bb.t+context.padding.y], Color.black); + context.bb = bb; + end(); +} + +mum.label = function(str, data = {}) +{ + if (pre(data)) return; + render.set_font(data.font, data.font_size); + context.bb = render.text(str, cursor, context.size, context.color); + + end(); } \ No newline at end of file diff --git a/scripts/render.js b/scripts/render.js index 3faed566..a5170b73 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -361,11 +361,13 @@ render.device.doc = `Device resolutions given as [x,y,inches diagonal].`; var textshader; var circleshader; var polyshader; +var slice9shader; render.init = function() { textshader = render.make_shader("shaders/text_base.cg"); render.spriteshader = render.make_shader("shaders/sprite.cg"); render.postshader = render.make_shader("shaders/simplepost.cg"); + slice9shader = render.make_shader("shaders/9slice.cg"); circleshader = render.make_shader("shaders/circle.cg"); polyshader = render.make_shader("shaders/poly.cg"); @@ -498,20 +500,27 @@ render.window = function(pos, wh, color) { }; render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0,1], cursor = -1) { + var bb = render.text_size(str, size, wrap); - var w = bb.r*2; - var h = bb.t*2; + var w = (bb.r - bb.l); + var h = (bb.t - bb.b); //render.text draws with an anchor on top left corner var p = pos.slice(); + bb.r += pos.x; + bb.l += pos.x; + bb.t += pos.y; + bb.b += pos.y; + gui.text(str, p, size, color, wrap, cursor); + return bb; + p.x -= w * anchor.x; bb.r += (w*anchor.x); bb.l += (w*anchor.x); p.y += h * (1 - anchor.y); bb.t += h*(1-anchor.y); bb.b += h*(1-anchor.y); - gui.text(str, p, size, color, wrap, cursor); - + return bb; }; @@ -528,12 +537,49 @@ render.image = function(tex, pos, scale = 1, rotation = 0, color = Color.white, var bind = render.sg_bind(render.spriteshader, shape.quad, {diffuse:tex}); bind.inst = 1; render.spdraw(bind); + + var bb = {}; + bb.b = pos.y; + bb.l = pos.x; + bb.t = pos.y + tex.height*scale; + bb.r = pos.x + tex.width*scale; + return bb; +} + +render.slice9 = function(tex, pos, bb, scale = 1, color = Color.white) +{ + var t = os.make_transform(); + t.pos = pos; + t.scale = [scale,scale,scale]; + render.setpipeline(render.slice9.pipe); + render.setunim4(0, render.slice9.vs.unimap.model.slot, t); + render.shader_apply_material(render.slice9, { + shade: color + }); + var bind = render.sg_bind(render.slice9, shape.quad, {diffuse:tex}); + bind.inst = 1; + render.spdraw(bind); +} + +var textssbo = render.text_ssbo(); + +render.flush_text = function() +{ + if (!render.textshader) return; + render.setpipeline(render.textshader.pipe); + render.shader_apply_material(render.textshader); + var textbind = render.sg_bind(render.textshader, shape.quad, {text:render.font.texture}, textssbo); + textbind.inst = render.flushtext(); + render.spdraw(textbind); } render.fontcache = {}; render.set_font = function(path, size) { var fontstr = `${path}-${size}`; + if (render.font && render.fontcache[fontstr] === render.font) return; if (!render.fontcache[fontstr]) render.fontcache[fontstr] = os.make_font(path, size); + + render.flush_text(); gui.font_set(render.fontcache[fontstr]); render.font = render.fontcache[fontstr]; diff --git a/shaders/9slice.cg b/shaders/9slice.cg new file mode 100644 index 00000000..a2b4c657 --- /dev/null +++ b/shaders/9slice.cg @@ -0,0 +1,41 @@ +@block vert +uniform vec4 rect; +uniform vec2 diffuse_size; +void vert() +{ + pos *= vec3(diffuse_size*rect.zw, 1); + uv = (uv*rect.zw)+rect.xy; +} +@end + +@block frag +uniform vec4 border; +// borders in pixels, x = left, y = bottom, z = right, w = top +#define B vec4(10., 20., 30., 20.) + +vec2 uv9slice(vec2 uv, vec2 s, vec4 b) +{ + vec2 t = clamp((s * uv - b.xy) / (s - b.xy - b.zw), 0., 1.); + return mix(uv * s, 1. - s * (1. - uv), t); +} + +void frag() +{ + vec2 uv = fragCoord/iResolution.xy; + vec2 ts = vec2(textureSize(iChannel0, 0)); + // scaling factor + // probably available as uniform irl + vec2 s = iResolution.xy / ts; + + // border by texture size, shouldn't be > .5 + // probably available as uniform irl + vec4 b = min(B / ts.xyxy, vec4(.499)); + uv = uv9slice(uv, s, b); + + vec3 col = vec3(texture(iChannel0, uv).x); + + fragColor = vec4(col,1.0); +} +@end + +#include \ No newline at end of file diff --git a/shaders/basetext.cg b/shaders/basetext.cg index 7835168f..fcad25d2 100644 --- a/shaders/basetext.cg +++ b/shaders/basetext.cg @@ -1,4 +1,5 @@ #depth off +#blend mix @vs vs in vec2 a_pos; @@ -53,8 +54,8 @@ sampler smp; void main() { float lettera = texture(sampler2D(text,smp),fuv).r; - if (lettera < 0.1f) discard; frag(); + color.a = lettera; } @end diff --git a/shaders/sprite.cg b/shaders/sprite.cg index 98e4203a..8633fd5d 100644 --- a/shaders/sprite.cg +++ b/shaders/sprite.cg @@ -1,5 +1,4 @@ @block vert -uniform vec4 emissive; uniform vec4 rect; uniform vec2 diffuse_size; void vert() diff --git a/source/engine/font.c b/source/engine/font.c index 9823a956..0c8344f2 100644 --- a/source/engine/font.c +++ b/source/engine/font.c @@ -80,7 +80,7 @@ struct sFont *MakeSDFFont(const char *fontfile, int height) } struct sFont *MakeFont(const char *fontfile, int height) { - int packsize = 1024; + int packsize = 2048; struct sFont *newfont = calloc(1, sizeof(struct sFont)); newfont->height = height; @@ -106,11 +106,16 @@ struct sFont *MakeFont(const char *fontfile, int height) { if (!stbtt_InitFont(&fontinfo, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0))) { YughError("Failed to make font %s", fontfile); } + + int ascent, descent, linegap; - stbtt_GetFontVMetrics(&fontinfo, &newfont->ascent, &newfont->descent, &newfont->linegap); - //newfont->emscale = stbtt_ScaleForMappingEmToPixels(&fontinfo, 16); - newfont->emscale = stbtt_ScaleForPixelHeight(&fontinfo, height); - newfont->linegap = (newfont->ascent - newfont->descent) * newfont->emscale*1.5; + stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &linegap); + float emscale = stbtt_ScaleForPixelHeight(&fontinfo, height); + newfont->ascent = ascent*emscale; + newfont->descent = descent*emscale; + newfont->linegap = linegap*emscale; + newfont->linegap = ((newfont->ascent - newfont->descent) - newfont->linegap); + printf("newfont : %g, %g, %g\n", newfont->ascent, newfont->descent, newfont->linegap); newfont->texture = malloc(sizeof(texture)); newfont->texture->id = sg_make_image(&(sg_image_desc){ @@ -137,12 +142,9 @@ struct sFont *MakeFont(const char *fontfile, int height) { r.y = (glyph.y0) / (float)packsize; r.h = (glyph.y1-glyph.y0) / (float)packsize; - stbtt_GetCodepointHMetrics(&fontinfo, c, &newfont->Characters[c].Advance, &newfont->Characters[c].leftbearing); - newfont->Characters[c].leftbearing *= newfont->emscale; - newfont->Characters[c].Advance = glyph.xadvance; /* x distance from this char to the next */ - newfont->Characters[c].Size[0] = glyph.x1 - glyph.x0; - newfont->Characters[c].Size[1] = glyph.y1 - glyph.y0; + newfont->Characters[c].Size[0] = (glyph.x1 - glyph.x0); + newfont->Characters[c].Size[1] = (glyph.y1 - glyph.y0); newfont->Characters[c].Bearing[0] = glyph.xoff; newfont->Characters[c].Bearing[1] = glyph.yoff2; newfont->Characters[c].rect = r; @@ -202,8 +204,6 @@ void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgb struct text_vert vert; - float lsize = 1.0 / 1024.0; - vert.pos.x = cursor.X + c.Bearing[0] * scale; vert.pos.y = cursor.Y - c.Bearing[1] * scale; vert.wh.x = c.Size[0] * scale; @@ -290,7 +290,12 @@ struct boundingbox text_bb(const char *text, float scale, float lw, float tracki } } - return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){cursor.X,use_font->linegap-cursor.Y}); + return (struct boundingbox){ + .b = cursor.Y + use_font->descent, + .t = cursor.Y + use_font->ascent, + .l = 0, + .r = cursor.X + }; } void check_caret(int caret, int l, HMM_Vec2 pos, float scale, struct rgba color) diff --git a/source/engine/font.h b/source/engine/font.h index 692eba5b..db619885 100644 --- a/source/engine/font.h +++ b/source/engine/font.h @@ -22,10 +22,9 @@ struct Character { struct sFont { uint32_t fontTexture; uint32_t height; /* in pixels */ - int ascent; - int descent; - int linegap; - float emscale; + float ascent; + float descent; + float linegap; struct Character Characters[256]; sg_image texID; texture *texture; diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index b26cd67b..b871d662 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -617,7 +617,6 @@ int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) { return best; } - JSC_GETSET(warp_gravity, strength, number) JSC_GETSET(warp_gravity, decay, number) JSC_GETSET(warp_gravity, spherical, boolean)