New mum tools
This commit is contained in:
parent
dfec02ebcb
commit
9a98ce5e43
6
Makefile
6
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)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# spline
|
||||
#### catmull()
|
||||
|
||||
|
||||
|
||||
#### bezier()
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# vector
|
||||
#### dot()
|
||||
|
||||
|
||||
|
||||
#### project()
|
||||
|
||||
|
||||
|
||||
#### inflate()
|
||||
|
||||
|
||||
|
||||
|
|
@ -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];
|
||||
|
|
|
@ -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 };
|
||||
|
|
116
scripts/mum.js
116
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();
|
||||
}
|
|
@ -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
41
shaders/9slice.cg
Normal 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>
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@block vert
|
||||
uniform vec4 emissive;
|
||||
uniform vec4 rect;
|
||||
uniform vec2 diffuse_size;
|
||||
void vert()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue