New mum tools

This commit is contained in:
John Alanbrook 2024-07-09 01:03:39 -05:00
parent dfec02ebcb
commit 9a98ce5e43
13 changed files with 240 additions and 114 deletions

View file

@ -200,6 +200,9 @@ endif
all: $(NAME) all: $(NAME)
cp -f $(NAME) $(APP)$(EXT) cp -f $(NAME) $(APP)$(EXT)
$(APP): $(NAME)
cp -f $(NAME) $(APP)
$(NAME): $(OBJS) $(DEPS) $(NAME): $(OBJS) $(DEPS)
@echo Linking $(NAME) @echo Linking $(NAME)
$(CROSS)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@ $(CROSS)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@
@ -282,6 +285,9 @@ clean:
docs: doc/prosperon.org docs: doc/prosperon.org
make -C doc make -C doc
mv doc/html . 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]" TAGINC != find . -name "*.[chj]"
tags: $(TAGINC) tags: $(TAGINC)

View file

@ -1,10 +0,0 @@
# spline
#### catmull()
#### bezier()

View file

@ -1,14 +0,0 @@
# vector
#### dot()
#### project()
#### inflate()

View file

@ -241,24 +241,9 @@ function use(file, env = {}, script) {
use.cache = {}; use.cache = {};
global.check_registers = function (obj) { global.check_registers = function (obj) {
if (typeof obj.update === "function") for (var reg in Register.registries)
obj.timers.push(Register.update.register(obj.update.bind(obj))); if (typeof obj[reg] === 'function')
obj.timers.push(Register.registries[reg].register(obj[reg].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 k in obj) { for (var k in obj) {
if (!k.startswith("on_")) continue; if (!k.startswith("on_")) continue;
var signal = k.fromfirst("on_"); var signal = k.fromfirst("on_");
@ -363,11 +348,7 @@ function process() {
} }
var st = profile.now(); var st = profile.now();
prosperon.window_render(window.size); prosperon.window_render(window.size);
prosperon.draw(); prosperon.render();
prosperon.debug();
prosperon.gui();
prosperon.screengui();
prosperon.hookend?.();
profile.addreport(profcache, "render frame", st); profile.addreport(profcache, "render frame", st);
frames.push(profile.secs(profile.now() - startframe)); frames.push(profile.secs(profile.now() - startframe));
if (frames.length > 20) frames.shift(); if (frames.length > 20) frames.shift();
@ -558,7 +539,7 @@ which returns a function that, when invoked, cancels the registry.
var Register = { var Register = {
registries: [], registries: [],
add_cb(name) { add_cb(name, e_event = false) {
var n = {}; var n = {};
var fns = []; var fns = [];
@ -579,19 +560,19 @@ var Register = {
}; };
Register[name] = n; Register[name] = n;
Register.registries.push(n); Register.registries[name] = n;
return n; return n;
}, },
}; };
Register.add_cb("appupdate"); Register.add_cb("appupdate", true);
Register.add_cb("update").doc = "Called once per frame."; Register.add_cb("update", true).doc = "Called once per frame.";
Register.add_cb("physupdate"); Register.add_cb("physupdate", true);
Register.add_cb("gui"); Register.add_cb("gui", true);
Register.add_cb("debug"); Register.add_cb("hud", true);
Register.add_cb("draw"); Register.add_cb("debug", true);
Register.add_cb("screengui"); Register.add_cb("draw", true);
var Event = { var Event = {
events: {}, events: {},
@ -643,8 +624,7 @@ function world_start() {
global.mixin("scripts/physics"); global.mixin("scripts/physics");
global.mixin("scripts/widget"); global.mixin("scripts/widget");
global.mixin("scripts/mum");
globalThis.mum = app.spawn("scripts/mum");
window.title = `Prosperon v${prosperon.version}`; window.title = `Prosperon v${prosperon.version}`;
window.size = [500, 500]; window.size = [500, 500];

View file

@ -164,16 +164,6 @@ Mum.button = Mum.text._int.extend({
action() { console.warn("Button has no action."); }, 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({ Mum.window = Mum.extend({
start() { start() {
this.wh = [this.width, this.height]; this.wh = [this.width, this.height];
@ -190,10 +180,7 @@ Mum.window = Mum.extend({
if (item.hide) return; if (item.hide) return;
item.draw(pos.slice(),this); item.draw(pos.slice(),this);
}, 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(); bind.inst = render.flushtext();
render.spdraw(bind); render.spdraw(bind);
gui.scissor_win(); gui.scissor_win();
@ -243,12 +230,13 @@ Mum.column = Mum.extend({
}, },
}); });
/*
Mum.debug_colors = { Mum.debug_colors = {
bounds: Color.red.slice(), bounds: Color.red.slice(),
margin: Color.blue.slice(), margin: Color.blue.slice(),
padding: Color.green.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 };

View file

@ -1,21 +1,107 @@
globalThis.mum = {};
var panel; var panel;
self.screengui = function() mum.base = {
{ padding:[0,0], /* Each element inset with this padding on all sides */
if (panel) panel.gui(); 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`); post();
panel = Object.create(listpanel); context = contexts.pop();
panel.title = msg; if (!context) context = mum.base;
panel.value = value; }
panel.allassets = list;
panel.action = function() { var listpost = function()
cb(panel.value); {
panel = undefined; var height = 0;
} if (context.height) height += context.height;
panel.start(); else height += (context.bb.t - context.bb.b);
player[0].control(panel); 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();
} }

View file

@ -361,11 +361,13 @@ render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
var textshader; var textshader;
var circleshader; var circleshader;
var polyshader; var polyshader;
var slice9shader;
render.init = function() { render.init = function() {
textshader = render.make_shader("shaders/text_base.cg"); textshader = render.make_shader("shaders/text_base.cg");
render.spriteshader = render.make_shader("shaders/sprite.cg"); render.spriteshader = render.make_shader("shaders/sprite.cg");
render.postshader = render.make_shader("shaders/simplepost.cg"); render.postshader = render.make_shader("shaders/simplepost.cg");
slice9shader = render.make_shader("shaders/9slice.cg");
circleshader = render.make_shader("shaders/circle.cg"); circleshader = render.make_shader("shaders/circle.cg");
polyshader = render.make_shader("shaders/poly.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) { 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 bb = render.text_size(str, size, wrap);
var w = bb.r*2; var w = (bb.r - bb.l);
var h = bb.t*2; var h = (bb.t - bb.b);
//render.text draws with an anchor on top left corner //render.text draws with an anchor on top left corner
var p = pos.slice(); 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; p.x -= w * anchor.x;
bb.r += (w*anchor.x); bb.r += (w*anchor.x);
bb.l += (w*anchor.x); bb.l += (w*anchor.x);
p.y += h * (1 - anchor.y); p.y += h * (1 - anchor.y);
bb.t += h*(1-anchor.y); bb.t += h*(1-anchor.y);
bb.b += h*(1-anchor.y); bb.b += h*(1-anchor.y);
gui.text(str, p, size, color, wrap, cursor);
return bb; 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}); var bind = render.sg_bind(render.spriteshader, shape.quad, {diffuse:tex});
bind.inst = 1; bind.inst = 1;
render.spdraw(bind); 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.fontcache = {};
render.set_font = function(path, size) { render.set_font = function(path, size) {
var fontstr = `${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); if (!render.fontcache[fontstr]) render.fontcache[fontstr] = os.make_font(path, size);
render.flush_text();
gui.font_set(render.fontcache[fontstr]); gui.font_set(render.fontcache[fontstr]);
render.font = render.fontcache[fontstr]; render.font = render.fontcache[fontstr];

41
shaders/9slice.cg Normal file
View file

@ -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 <base.cg>

View file

@ -1,4 +1,5 @@
#depth off #depth off
#blend mix
@vs vs @vs vs
in vec2 a_pos; in vec2 a_pos;
@ -53,8 +54,8 @@ sampler smp;
void main() void main()
{ {
float lettera = texture(sampler2D(text,smp),fuv).r; float lettera = texture(sampler2D(text,smp),fuv).r;
if (lettera < 0.1f) discard;
frag(); frag();
color.a = lettera;
} }
@end @end

View file

@ -1,5 +1,4 @@
@block vert @block vert
uniform vec4 emissive;
uniform vec4 rect; uniform vec4 rect;
uniform vec2 diffuse_size; uniform vec2 diffuse_size;
void vert() void vert()

View file

@ -80,7 +80,7 @@ struct sFont *MakeSDFFont(const char *fontfile, int height)
} }
struct sFont *MakeFont(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)); struct sFont *newfont = calloc(1, sizeof(struct sFont));
newfont->height = height; 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))) { if (!stbtt_InitFont(&fontinfo, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0))) {
YughError("Failed to make font %s", fontfile); YughError("Failed to make font %s", fontfile);
} }
int ascent, descent, linegap;
stbtt_GetFontVMetrics(&fontinfo, &newfont->ascent, &newfont->descent, &newfont->linegap); stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &linegap);
//newfont->emscale = stbtt_ScaleForMappingEmToPixels(&fontinfo, 16); float emscale = stbtt_ScaleForPixelHeight(&fontinfo, height);
newfont->emscale = stbtt_ScaleForPixelHeight(&fontinfo, height); newfont->ascent = ascent*emscale;
newfont->linegap = (newfont->ascent - newfont->descent) * newfont->emscale*1.5; 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 = malloc(sizeof(texture));
newfont->texture->id = sg_make_image(&(sg_image_desc){ 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.y = (glyph.y0) / (float)packsize;
r.h = (glyph.y1-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].Advance = glyph.xadvance; /* x distance from this char to the next */
newfont->Characters[c].Size[0] = glyph.x1 - glyph.x0; newfont->Characters[c].Size[0] = (glyph.x1 - glyph.x0);
newfont->Characters[c].Size[1] = glyph.y1 - glyph.y0; newfont->Characters[c].Size[1] = (glyph.y1 - glyph.y0);
newfont->Characters[c].Bearing[0] = glyph.xoff; newfont->Characters[c].Bearing[0] = glyph.xoff;
newfont->Characters[c].Bearing[1] = glyph.yoff2; newfont->Characters[c].Bearing[1] = glyph.yoff2;
newfont->Characters[c].rect = r; 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; struct text_vert vert;
float lsize = 1.0 / 1024.0;
vert.pos.x = cursor.X + c.Bearing[0] * scale; vert.pos.x = cursor.X + c.Bearing[0] * scale;
vert.pos.y = cursor.Y - c.Bearing[1] * scale; vert.pos.y = cursor.Y - c.Bearing[1] * scale;
vert.wh.x = c.Size[0] * 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) void check_caret(int caret, int l, HMM_Vec2 pos, float scale, struct rgba color)

View file

@ -22,10 +22,9 @@ struct Character {
struct sFont { struct sFont {
uint32_t fontTexture; uint32_t fontTexture;
uint32_t height; /* in pixels */ uint32_t height; /* in pixels */
int ascent; float ascent;
int descent; float descent;
int linegap; float linegap;
float emscale;
struct Character Characters[256]; struct Character Characters[256];
sg_image texID; sg_image texID;
texture *texture; texture *texture;

View file

@ -617,7 +617,6 @@ int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) {
return best; return best;
} }
JSC_GETSET(warp_gravity, strength, number) JSC_GETSET(warp_gravity, strength, number)
JSC_GETSET(warp_gravity, decay, number) JSC_GETSET(warp_gravity, decay, number)
JSC_GETSET(warp_gravity, spherical, boolean) JSC_GETSET(warp_gravity, spherical, boolean)