This commit is contained in:
John Alanbrook 2024-10-14 20:07:32 -05:00
parent 7f6996d93f
commit 2a1a5231aa
7 changed files with 132 additions and 224 deletions

View file

@ -873,6 +873,11 @@ render.rectangle = function render_rectangle(lowerleft, upperright, color, shade
check_flush(flush_poly);
};
render.brect = function(brect, color = Color.white)
{
render.rectangle([brect.x,brect.y], [brect.x+brect.width, brect.y+brect.height], color);
}
render.rect = function(rect, color, shader, pipe)
{
render.rectangle([rect.x-rect.w/2, rect.y-rect.h/2], [rect.x+rect.w/2, rect.y+rect.h/2], color, shader, pipe);
@ -890,32 +895,11 @@ render.window = function render_window(pos, wh, color) {
render.box(pos.add(wh.scale(0.5)), wh, color);
};
render.text_bb = function (str, size = 1, wrap = -1, pos = [0, 0]) {
var bb = render.text_size(str, size, wrap);
var w = bb.r - bb.l;
var h = bb.t - bb.b;
bb.r += pos.x;
bb.l += pos.x;
bb.t += pos.y;
bb.b += pos.y;
return bb;
};
render.text = function (str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0, 1], cursor = -1) {
var bb = render.text_bb(str, size, wrap, pos);
gui.text(str, pos, size, color, wrap, cursor); // this puts text into buffer
render.text = function (str, pos, font = cur_font, size = 1, color = Color.white, wrap = -1) {
if (!font) return;
gui.text(str, pos, size, color, wrap, font); // this puts text into buffer
cur_font = font;
check_flush(render.flush_text);
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);
return bb;
};
var lasttex = undefined;
@ -1063,7 +1047,10 @@ render.image = function image(image, pos, scale, rotation = 0, color = Color.whi
};
// pos is the lower left corner, scale is the width and height
render.slice9 = function (tex, pos, bb, scale = [tex.width, tex.height], color = Color.white) {
render.slice9 = function (image, pos, bb, scale = [tex.width, tex.height], color = Color.white) {
if (typeof image === 'string')
image = game.texture(image);
var tex = image.texture;
var t = os.make_transform();
t.pos = pos;
t.scale = [scale.x / tex.width, scale.y / tex.height, 1];
@ -1090,6 +1077,7 @@ function endframe() {
var textssbos = [];
var tdraw = 0;
var cur_font = undefined;
render.flush_text = function () {
if (!render.textshader) return;
@ -1105,22 +1093,18 @@ render.flush_text = function () {
}
render.use_shader(render.textshader);
render.use_mat({ text: render.font.texture });
render.use_mat({ text: cur_font.texture });
render.draw(shape.quad, textssbo, amt);
};
var fontcache = {};
render.set_font = function (path, size) {
render.get_font = function(path,size)
{
var fontstr = `${path}-${size}`;
if (render.font && fontcache[fontstr] === render.font) return;
if (!fontcache[fontstr]) fontcache[fontstr] = os.make_font(path, size);
render.flush_text();
gui.font_set(fontcache[fontstr]);
render.font = fontcache[fontstr];
};
if (!fontcache[fontstr]) fontcache[fontstr] = os.make_font(path,size);
return fontcache[fontstr];
}
render.doc = "Draw shapes in screen space.";
render.cross.doc = "Draw a cross centered at pos, with arm length size.";

View file

@ -269,7 +269,6 @@ Cmdline.register_order(
if (project.title) window.title = project.title;
game.engine_start(function () {
render.set_font("fonts/c64.ttf", 8);
if (io.exists("game.js")) global.app = actor.spawn("game.js");
else global.app = actor.spawn("scripts/nogame.js");

View file

@ -6,7 +6,7 @@ struct letter {
vec2 pos;
vec2 wh;
vec2 uv;
vec2 st;
vec2 uv_size;
vec4 color;
};
@ -14,8 +14,8 @@ readonly buffer ssbo {
letter ls[];
};
out vec2 uv;
out vec2 fuv;
out vec2 uv; // Normalized UV, from 0 to 1 on the letter, for special effects
out vec2 fuv; // This is the UV given to get the correct letter from the texture
out vec4 color0;
vec2 pos;
@ -26,11 +26,11 @@ uniform mat4 vp;
void main()
{
letter l = ls[gl_InstanceIndex];
fuv = l.uv + vec2(a_pos.x*l.st.x, l.st.y - a_pos.y*l.st.y);
letter charData = ls[gl_InstanceIndex];
fuv = charData.uv + vec2(a_pos.x*charData.uv_size.x, charData.uv_size.y - a_pos.y*charData.uv_size.y);
uv = a_uv;
color0 = l.color;
pos = l.pos+(a_pos*l.wh);
color0 = charData.color;
pos = charData.pos+(a_pos*charData.wh);
vert();
gl_Position = vp * vec4(pos, 0.0, 1.0);
}

View file

@ -261,6 +261,7 @@ typedef struct
uint16_t lineHeight;
Clay_TextElementConfigWrapMode wrapMode;
struct sFont *font;
JSValue jsstr;
#ifdef CLAY_EXTEND_CONFIG_ELEMENT
CLAY_EXTEND_CONFIG_ELEMENT
#endif

View file

@ -36,11 +36,6 @@ void font_free(font *f)
free(f);
}
void font_set(font *f)
{
use_font = f;
}
struct sFont *MakeSDFFont(const char *fontfile, int height)
{
YughInfo("Making sdf font %s.", fontfile);
@ -99,10 +94,11 @@ struct sFont *MakeFont(const char *fontfile, int height) {
int ascent, descent, linegap;
stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &linegap);
float emscale = tbtt_ScaleForMappingEmToPixels(&fontinfo, height);
float emscale = stbtt_ScaleForMappingEmToPixels(&fontinfo, height);
newfont->ascent = ascent*emscale;
newfont->descent = descent*emscale;
newfont->linegap = linegap*emscale;
printf("ascent %g descent %g linegap %g\n", newfont->ascent, newfont->descent, newfont->linegap);
newfont->texture = malloc(sizeof(texture));
newfont->texture->id = sg_make_image(&(sg_image_desc){
@ -129,6 +125,11 @@ struct sFont *MakeFont(const char *fontfile, int height) {
r.y = (glyph.y0) / (float)packsize;
r.h = (glyph.y1-glyph.y0) / (float)packsize;
newfont->Characters[c].size = (HMM_Vec2){
.x = glyph.x1-glyph.x0,
.y = glyph.y1-glyph.y0
};
newfont->Characters[c].Advance = glyph.xadvance; /* x distance from this char to the next */
newfont->Characters[c].rect = r;
}
@ -139,27 +140,6 @@ struct sFont *MakeFont(const char *fontfile, int height) {
return newfont;
}
void draw_underline_cursor(HMM_Vec2 pos, float scale, struct rgba color)
{
pos.Y -= 2;
sdrawCharacter(use_font->Characters['_'], pos, scale, color);
}
void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color)
{
HMM_Vec2 wh;
color.a = 30;
wh.x = c.Size[0] * scale + 2;
wh.y = c.Size[1] * scale + 2;
cursor.X += c.Bearing[0] * scale + 1;
cursor.Y -= (c.Bearing[1] * scale + 1);
HMM_Vec2 b;
b.x = cursor.X + wh.x/2;
b.y = cursor.Y + wh.y/2;
}
int text_flush(sg_buffer *buf) {
if (arrlen(text_buffer) == 0) return 0;
@ -183,14 +163,11 @@ int text_flush(sg_buffer *buf) {
}
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color) {
struct rgba colorbox = {0,0,0,255};
struct text_vert vert;
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;
vert.wh.y = c.Size[1] * scale;
vert.pos.x = cursor.X + c.leftbearing * scale;
vert.pos.y = cursor.Y - c.topbearing * scale;
vert.wh = c.size;
// if (vert.pos.x > frame.l || vert.pos.y > frame.t || (vert.pos.y + vert.wh.y) < frame.b || (vert.pos.x + vert.wh.x) < frame.l) return;
@ -230,92 +207,71 @@ const char *esc_color(const char *c, struct rgba *color, struct rgba defc)
return c;
}
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking)
// text is a string, font f, size is height in pixels, wrap is how long a line is before wrapping. -1to not wrap
HMM_Vec2 measure_text(const char *text, font *f, float size, float letterSpacing, float wrap)
{
if (!use_font) return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){0,0});
struct rgba dummy;
HMM_Vec2 cursor = {0,0};
const char *line, *wordstart;
line = text;
HMM_Vec2 dim = {0};
float maxWidth = 0; // max width of any line
float lineWidth = 0; // current line width
size = f->height;
while (*line != '\0') {
if (isblank(*line)) {
cursor.X += use_font->Characters[*line].Advance * tracking * scale;
line++;
} else if (isspace(*line)) {
cursor.Y -= scale * use_font->linegap;
cursor.X = 0;
line++;
} else {
if (*line == '\e')
line = esc_color(line, NULL, dummy);
float scale = size/f->height;
float lineHeight = f->ascent - f->descent;
lineHeight *= scale;
letterSpacing *= scale;
wordstart = line;
int wordWidth = 0;
float height = lineHeight; // total height
while (!isspace(*line) && *line != '\0') {
wordWidth += use_font->Characters[*line].Advance * tracking * scale;
line++;
}
if (lw > 0 && (cursor.X + wordWidth) >= lw) {
cursor.X = 0;
cursor.Y -= scale * use_font->linegap;
}
while (wordstart < line) {
if (*wordstart == '\e')
line = esc_color(wordstart, NULL, dummy);
cursor.X += use_font->Characters[*wordstart].Advance * tracking * scale;
wordstart++;
}
for (char *c = text; *c != 0; c++) {
if (*c == '\n') {
maxWidth = fmaxf(maxWidth, lineWidth);
lineWidth = 0;
height += lineHeight + f->linegap;
continue;
}
lineWidth += f->Characters[*c].Advance + letterSpacing;
}
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)
{
if (caret == l)
draw_underline_cursor(pos,scale,color);
maxWidth = fmaxf(maxWidth, lineWidth);
dim.x = maxWidth;
dim.y = height;
return dim;
}
/* pos given in screen coordinates */
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking) {
if (!use_font) {
YughError("Cannot render text before a font is set.");
return pos.y;
}
void renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap) {
int len = strlen(text);
HMM_Vec2 cursor = pos;
float lineHeight = f->ascent - f->descent;
for (char *c = text; *c != 0; c++) {
if (*c == '\n') {
cursor.x = pos.x;
cursor.y += lineHeight + f->linegap;
continue;
}
sdrawCharacter(f->Characters[*c], cursor, scale, color);
cursor.x += f->Characters[*c].Advance;
}
return;
const char *line, *wordstart, *drawstart;
line = drawstart = text;
struct rgba usecolor = color;
check_caret(caret, line-drawstart, cursor, scale, usecolor);
while (*line != '\0') {
if (isblank(*line)) {
sdrawCharacter(use_font->Characters[*line], cursor, scale, usecolor);
cursor.X += use_font->Characters[*line].Advance * tracking * scale;
sdrawCharacter(f->Characters[*line], cursor, scale, usecolor);
cursor.X += f->Characters[*line].Advance * scale;
line++;
check_caret(caret, line-drawstart, cursor, scale, usecolor);
} else if (isspace(*line)) {
sdrawCharacter(use_font->Characters[*line], cursor, scale, usecolor);
cursor.Y -= scale * use_font->linegap;
sdrawCharacter(f->Characters[*line], cursor, scale, usecolor);
cursor.Y -= scale * f->linegap;
cursor.X = pos.X;
line++;
check_caret(caret, line-drawstart, cursor, scale, usecolor);
} else {
if (*line == '\e')
line = esc_color(line, &usecolor, color);
@ -325,28 +281,25 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f
while (!isspace(*line) && *line != '\0') {
wordWidth += use_font->Characters[*line].Advance * tracking * scale;
wordWidth += f->Characters[*line].Advance * scale;
line++;
}
if (lw > 0 && (cursor.X + wordWidth - pos.X) >= lw) {
if (wrap > 0 && (cursor.X + wordWidth - pos.X) >= wrap) {
cursor.X = pos.X;
cursor.Y -= scale * use_font->linegap;
cursor.Y -= scale * f->linegap;
}
while (wordstart < line) {
if (*wordstart == '\e')
wordstart = esc_color(wordstart, &usecolor, color);
//sdrawCharacter(use_font->Characters[*wordstart], HMM_AddV2(cursor, HMM_MulV2F((HMM_Vec2){1,-1},scale)), scale, (rgba){0,0,0,255});
sdrawCharacter(use_font->Characters[*wordstart], cursor, scale, usecolor);
//sdrawCharacter(f->Characters[*wordstart], HMM_AddV2(cursor, HMM_MulV2F((HMM_Vec2){1,-1},scale)), scale, (rgba){0,0,0,255});
sdrawCharacter(f->Characters[*wordstart], cursor, scale, usecolor);
cursor.X += use_font->Characters[*wordstart].Advance * tracking * scale;
cursor.X += f->Characters[*wordstart].Advance * scale;
wordstart++;
check_caret(caret, wordstart-drawstart, cursor, scale, usecolor);
}
}
}
return cursor.Y - pos.Y;
}

View file

@ -12,10 +12,11 @@ extern sg_buffer text_ssbo;
/// Holds all state information relevant to a character as loaded using FreeType
struct Character {
int Advance; // Horizontal offset to advance to next glyph
float Advance; // Horizontal offset to advance to next glyph
float leftbearing; // X offset from cursor to render at
float topbearing; // Y offset from cursor to render at
struct rect rect; // the rect on the font image to render from
struct rect rect; // the rect on the font image to render from, uv coordinates
HMM_Vec2 size; // The pixel size of this letter
};
struct sFont {
@ -29,16 +30,16 @@ struct sFont {
};
typedef struct sFont font;
typedef struct Character glyph;
void font_free(font *f);
struct sFont *MakeFont(const char *fontfile, int height);
void font_set(font *f);
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color);
void text_settype(struct sFont *font);
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking);
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking);
void renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap);
HMM_Vec2 measure_text(const char *text, font *f, float scale, float letterSpacing, float wrap);
// Flushes all letters from renderText calls into the provided buffer
int text_flush(sg_buffer *buf);
#endif

View file

@ -788,7 +788,9 @@ JSC_CCALL(render_viewport,
JSC_CCALL(render_commit, sg_commit())
JSC_CCALL(render_end_pass, sg_end_pass())
JSC_SCALL(render_text_size, ret = bb2js(text_bb(str, js2number(argv[1]), js2number(argv[2]), 1)))
JSC_SCALL(render_measure_text,
ret = vec22js(measure_text(str, js2font(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4])))
)
HMM_Mat4 transform2view(transform *t)
{
@ -1263,7 +1265,7 @@ static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, end_pass, 0),
MIST_FUNC_DEF(render, commit, 0),
MIST_FUNC_DEF(render, glue_pass, 0),
MIST_FUNC_DEF(render, text_size, 3),
MIST_FUNC_DEF(render, measure_text, 5),
MIST_FUNC_DEF(render, set_camera, 1),
MIST_FUNC_DEF(render, make_pipeline, 1),
MIST_FUNC_DEF(render, setuniv3, 2),
@ -1387,25 +1389,18 @@ JSC_CCALL(gui_scissor,
sg_apply_scissor_rect(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0);
)
JSC_CCALL(gui_text,
const char *s = JS_ToCString(js, argv[0]);
JSC_SCALL(gui_text,
HMM_Vec2 pos = js2vec2(argv[1]);
float size = js2number(argv[2]);
struct rgba c = js2color(argv[3]);
int wrap = js2number(argv[4]);
int cursor = js2number(argv[5]);
JSValue ret = JS_NewInt64(js, renderText(s, pos, size, c, wrap, cursor, 1.0));
JS_FreeCString(js, s);
return ret;
font *f = js2font(argv[5]);
renderText(str, pos, f, size, c, wrap);
)
JSC_CCALL(gui_font_set, font_set(js2font(argv[0])))
static const JSCFunctionListEntry js_gui_funcs[] = {
MIST_FUNC_DEF(gui, scissor, 4),
MIST_FUNC_DEF(gui, text, 6),
MIST_FUNC_DEF(gui, font_set,1)
};
JSC_CCALL(spline_catmull,
@ -3818,15 +3813,18 @@ JSC_CCALL(clay_dimensions,
Clay_SetLayoutDimensions((Clay_Dimensions) { dim.x, dim.y });
)
static int container_id = 0;
JSC_CCALL(clay_draw,
container_id = 0;
Clay_BeginLayout();
script_call_sym(argv[0], 0, NULL);
Clay_RenderCommandArray cmd = Clay_EndLayout();
ret = JS_NewArray(js);
printf("there are %d commands here\n", cmd.length);
for (int i = 0; i < cmd.length; i++) {
Clay_RenderCommand cc = cmd.internalArray[i];
if (cc.commandType == CLAY_RENDER_COMMAND_TYPE_NONE) continue;
JSValue c = JS_NewObject(js);
JSValue bb = JS_NewObject(js);
js_setpropstr(bb, "x", number2js(cc.boundingBox.x));
@ -3835,9 +3833,29 @@ JSC_CCALL(clay_draw,
js_setpropstr(bb, "height", number2js(cc.boundingBox.height));
js_setpropstr(c, "boundingbox", bb);
js_setprop_num(ret, i, c);
Clay_RectangleElementConfig *rect = cc.config.rectangleElementConfig;
js_setpropstr(c, "config", rect->js);
switch(cc.commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:
Clay_RectangleElementConfig *rect = cc.config.rectangleElementConfig;
js_setpropstr(c, "config", JS_DupValue(js,rect->js));
break;
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START:
js_setpropstr(c, "config", str2js("scissor_start"));
break;
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END:
js_setpropstr(c, "config", str2js('scissor_end'));
break;
}
}
for (int i = 0; i < cmd.length; i++) {
Clay_RenderCommand cc = cmd.internalArray[i];
if (cc.commandType != CLAY_RENDER_COMMAND_TYPE_RECTANGLE) continue;
Clay_RectangleElementConfig *rect = cc.config.rectangleElementConfig;
if (!JS_IsUndefined(rect->js)) {
JS_FreeValue(js, rect->js);
rect->js = JS_UNDEFINED;
}
}
)
JSC_CCALL(clay_pointer,
@ -3853,41 +3871,15 @@ JSC_CCALL(clay_updatescroll,
Clay_UpdateScrollContainers(drag, (Clay_Vector2){delta.x,delta.y}, dt);
)
JSC_SCALL(clay_container,
Clay_LayoutConfig config = js2layout(argv[1]);
JSC_CCALL(clay_container,
Clay_LayoutConfig config = js2layout(argv[0]);
Clay_RectangleElementConfig rect = {0};
rect.js = JS_DupValue(js, argv[1]);
Clay_String cstr;
cstr.length = strlen(str);
cstr.chars = str;
// Clay__OpenRectangleElement(Clay__HashString(cstr,0,0), &config, &rect);
Clay__OpenRectangleElement(CLAY_ID("TEST"), &config, &rect);
script_call_sym(argv[2], 0, NULL);
rect.js = JS_DupValue(js, argv[0]);
Clay__OpenRectangleElement(CLAY_IDI_LOCAL("container", container_id++), &config, &rect);
script_call_sym(argv[1], 0, NULL);
Clay__CloseElementWithChildren();
)
JSC_SCALL(clay_image,
Clay_LayoutConfig config = js2layout(argv[1]);
Clay_ImageElementConfig image = {0};
HMM_Vec2 dim = js2vec2(argv[2]);
image.sourceDimensions.width = dim.x;
image.sourceDimensions.height = dim.y;
Clay__OpenImageElement(CLAY_ID(str), &config, &image);
script_call_sym(argv[3], 0, NULL);
Clay__CloseElementWithChildren();
)
JSC_SSCALL(clay_text,
Clay_TextElementConfig text = {0};
text.fontSize = js2number(js_getpropstr(argv[2], "font_size"));
text.letterSpacing = js2number(js_getpropstr(argv[2], "letter_spacing"));
text.lineHeight = js2number(js_getpropstr(argv[2], "line_spacing"));
text.wrapMode = (Clay_TextElementConfigWrapMode)js2number(js_getpropstr(argv[2], "wrap"));
text.font = js2font(js_getpropstr(argv[2], "font"));
Clay__OpenTextElement(CLAY_ID(str), CLAY_STRING(str2), &text);
// CLAY_TEXT(CLAY_ID(str), CLAY_STRING(str2), text)
)
JSC_SCALL(clay_scroll,
Clay_LayoutConfig config = js2layout(argv[1]);
Clay_ScrollElementConfig scroll = {0};
@ -3926,32 +3918,11 @@ static const JSCFunctionListEntry js_clay_funcs[] = {
MIST_FUNC_DEF(clay, draw, 1),
MIST_FUNC_DEF(clay, pointer, 2),
MIST_FUNC_DEF(clay, updatescroll, 3),
MIST_FUNC_DEF(clay, container, 3),
MIST_FUNC_DEF(clay, image, 4),
MIST_FUNC_DEF(clay, text, 3),
MIST_FUNC_DEF(clay, container, 2),
MIST_FUNC_DEF(clay, scroll, 3),
MIST_FUNC_DEF(clay, floating, 3),
};
// Example measure text function
static inline Clay_Dimensions MeasureText(Clay_String *text, Clay_TextElementConfig *config) {
// Clay_TextElementConfig contains members such as fontId, fontSize, letterSpacing etc
// Note: Clay_String->chars is not guaranteed to be null terminated
Clay_Dimensions size = {0};
float maxWidth = 0;
float lineWidth = 0;
float height = config->font->height;
float scale = config->fontSize/height;
float lineHeight = config->font->ascent + config->font->descent + config->font->linegap;
lineHeight *= config->lineHeight * scale;
float letterSpacing = config->letterSpacing * scale;
for (int i = 0; i < text->length; i++) {
}
}
#include "steam.h"
#define JSSTATIC(NAME, PARENT) \
@ -4006,7 +3977,6 @@ void ffi_load() {
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
Clay_Initialize(arena, (Clay_Dimensions) { 1920, 1080 });
Clay_SetMeasureTextFunction(MeasureText);
QJSGLOBALCLASS(clay);
QJSGLOBALCLASS(poly2d);