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

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 = {};
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];

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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