diff --git a/bind/v4k.lua b/bind/v4k.lua index 97632b0..6f0593e 100644 --- a/bind/v4k.lua +++ b/bind/v4k.lua @@ -1515,11 +1515,16 @@ ffi.cdef([[ //lcpp INF [0000] vec2: macro name but used as C declaration in:vec2* uvs; //lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 bounds; //lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 core; +//lcpp INF [0000] vec2: macro name but used as C declaration in:vec2 pivot; //lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 gamepad; //lcpp INF [0000] vec2: macro name but used as C declaration in:vec2 fire; //lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 pos; //lcpp INF [0000] vec2: macro name but used as C declaration in:vec2 sca; -//lcpp INF [0000] vec4: macro name but used as C declaration in:void (*draw_rect_func)(void* userdata, const char *skin, vec4 rect); +//lcpp INF [0000] vec4: macro name but used as C declaration in:void (*drawrect)(void* userdata, const char *skin, vec4 rect); +//lcpp INF [0000] vec2: macro name but used as C declaration in:void (*getskinsize)(void* userdata, const char *skin, vec2 *size); +//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 gui_getskinsize(const char *skin); +//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 gui_getskinsize(const char *skin); +//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 gui_getskinsize(const char *skin); //lcpp INF [0000] vec4: macro name but used as C declaration in:API void gui_panel(int id, vec4 rect, const char *skin); //lcpp INF [0000] vec4: macro name but used as C declaration in:STATIC void gui_panel(int id, vec4 rect, const char *skin); //lcpp INF [0000] vec4: macro name but used as C declaration in: void gui_panel(int id, vec4 rect, const char *skin); @@ -1621,12 +1626,12 @@ ffi.cdef([[ //lcpp INF [0000] vec3: macro name but used as C declaration in:API vec3 editor_pick(float mouse_x, float mouse_y); //lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC vec3 editor_pick(float mouse_x, float mouse_y); //lcpp INF [0000] vec3: macro name but used as C declaration in: vec3 editor_pick(float mouse_x, float mouse_y); -//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 editor_glyph(int x, int y, unsigned cp); -//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 editor_glyph(int x, int y, unsigned cp); -//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 editor_glyph(int x, int y, unsigned cp); -//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 editor_glyphstr(int x, int y, const char *utf8); -//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 editor_glyphstr(int x, int y, const char *utf8); -//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 editor_glyphstr(int x, int y, const char *utf8); +//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); +//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); +//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); +//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); +//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); +//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); typedef struct FILE FILE; typedef long int ptrdiff_t; typedef long unsigned int size_t; @@ -3257,6 +3262,7 @@ typedef struct atlas_slice_frame_t { vec4 bounds; bool has_9slice; vec4 core; +vec2 pivot; } atlas_slice_frame_t; typedef struct atlas_slice_t { unsigned name; @@ -3288,7 +3294,7 @@ unsigned play; bool paused; struct atlas_t *a; } sprite_t; -enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3937)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3937)___COUNTER__;; +enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3938)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3938)___COUNTER__;; void sprite_ctor(sprite_t *s); void sprite_dtor(sprite_t *s); void sprite_tick(sprite_t *s); @@ -3298,12 +3304,14 @@ enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on void sprite_del(sprite_t *s); void sprite_setanim(sprite_t *s, unsigned name); typedef struct guiskin_t { -void (*draw_rect_func)(void* userdata, const char *skin, vec4 rect); +void (*drawrect)(void* userdata, const char *skin, vec4 rect); +void (*getskinsize)(void* userdata, const char *skin, vec2 *size); void (*free)(void* userdata); void *userdata; } guiskin_t; void gui_pushskin(guiskin_t skin); void* gui_userdata(); + vec2 gui_getskinsize(const char *skin); void gui_panel(int id, vec4 rect, const char *skin); bool gui_button(int id, vec4 rect, const char *skin); void gui_popskin(); @@ -3679,8 +3687,8 @@ EDITOR_WINDOW_NK_SMALL, vec3 editor_pick(float mouse_x, float mouse_y); char* editor_path(const char *path); void editor_setmouse(int x, int y); - vec2 editor_glyph(int x, int y, unsigned cp); - vec2 editor_glyphstr(int x, int y, const char *utf8); + vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); + vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); void editor_gizmos(int dim); int editor_send(const char *cmd); const char* editor_recv(int jobid, double timeout_ss); diff --git a/demos/99-gui.c b/demos/99-gui.c index 17c1bb5..c4157a0 100644 --- a/demos/99-gui.c +++ b/demos/99-gui.c @@ -37,11 +37,16 @@ int main() { printf("%s\n", "Button pressed!"); } - gui_panel(vec4(40,140, 320, 40), "vial"); - gui_panel(vec4(40+9*skinned->scale,140+2*skinned->scale, 200, 64), "hp"); + gui_panel(vec4(40,140, 320, 20*skinned->scale), "vial"); + gui_panel(vec4(40,140, 200, 14*skinned->scale), "hp"); + gui_panel(vec4(40,240, 240, 20*skinned->scale), "vial"); + gui_panel(vec4(40,240, 160, 14*skinned->scale), "mp"); - gui_panel(vec4(40,230, 280, 40), "vial"); - gui_panel(vec4(40+9*skinned->scale,230+2*skinned->scale, 280-18*skinned->scale, 64), "mp"); + vec2 badge_size = gui_getskinsize("badge"); + badge_size.x += 2; // padding + gui_panel(vec4(60+badge_size.x*0,320, 1, 1), "badge"); + gui_panel(vec4(60+badge_size.x*1,320, 1, 1), "badge"); + gui_panel(vec4(60+badge_size.x*2,320, 1, 1), "badge_empty"); } gui_popskin(); diff --git a/demos/art/gui/golden.ase b/demos/art/gui/golden.ase index 5a3a764..5af7172 100644 Binary files a/demos/art/gui/golden.ase and b/demos/art/gui/golden.ase differ diff --git a/engine/editor.c b/engine/editor.c index 866d2b0..d43efa7 100644 --- a/engine/editor.c +++ b/engine/editor.c @@ -162,8 +162,8 @@ int editor_toolbar(int x, int y, int incw, int inch, const char *sym) { for each_array(codepoints, uint32_t, g) { int selected = oo ? is_hovering(vec4(ix,iy,ix+inc,iy+inc),vec2(ox,oy)) : 0; int hovering = dragging ? 0 : is_hovering(vec4(ix,iy,ix+inc,iy+inc), vec2(mx,my)); - const char *str8 = va("%s%s", selected || hovering ? FONT_COLOR1 : FONT_COLOR2, codepoint_to_utf8(g)); - editor_glyphstr(ix + inc/8, iy + inc/3 + 2, str8); + const char *str8 = codepoint_to_utf8(g); + editor_glyphs(ix + inc/8, iy + inc/3 + 2, selected || hovering ? FONT_COLOR1 FONT_H1 : FONT_COLOR2 FONT_H1, str8); ix += incw; iy += inch; } @@ -251,13 +251,13 @@ int lit_edit(lit *obj) { ICON_MDI_WEATHER_SUNNY // directional ICON_MDI_LIGHTBULB_FLUORESCENT_TUBE_OUTLINE ; - // editor_glyphstr(obj->pos.x+16,obj->pos.y-32,all_icons); + // editor_glyphs(obj->pos.x+16,obj->pos.y-32,all_icons); if( editor_selected(obj) ) { obj->pos.x += input(KEY_RIGHT) - input(KEY_LEFT); obj->pos.y += input(KEY_DOWN) - input(KEY_UP); obj->type = (obj->type + !!input_down(KEY_SPACE)) % 4; } - editor_glyphstr(obj->pos.x,obj->pos.y,lit_icon(obj)); + editor_glyphs(obj->pos.x,obj->pos.y,0,lit_icon(obj)); @@ -331,7 +331,7 @@ int kid_edit(kid *obj) { obj->pos.x += input(KEY_RIGHT) - input(KEY_LEFT); obj->pos.y += input(KEY_DOWN) - input(KEY_UP); - editor_glyphstr(obj->pos.x+16,obj->pos.y-16,ICON_MD_VIDEOGAME_ASSET); + editor_glyphs(obj->pos.x+16,obj->pos.y-16,0,ICON_MD_VIDEOGAME_ASSET); } return 1; } @@ -474,9 +474,9 @@ int main(){ int choice1 = editor_toolbar(window_width()-32, ui_has_menubar() ? 34 : 0, 0, 32, ICON_MD_VISIBILITY -ICON_MDI_ORBIT// ICON_MD_360 - ICON_MD_ZOOM_IN // ICON_MD_ZOOM_OUT_MAP - ICON_MD_GRID_ON ); // ICON_MDI_LOUPE ICON_MDI_GRID ); + ICON_MD_360 // ICON_MDI_ORBIT + ICON_MD_LOUPE // ZOOM_OUT_MAP // ICON_MD_ZOOM_IN + ICON_MD_GRID_ON ); // ICON_MDI_GRID ); int choice2 = editor_toolbar(window_width()-32*2, ui_has_menubar() ? 34 : 0, -32, 0, ICON_MD_SQUARE_FOOT ); if( choice1 > 0 ) { // clicked[>0] diff --git a/engine/joint/v4k.h b/engine/joint/v4k.h index 7c4d96e..54af6e3 100644 --- a/engine/joint/v4k.h +++ b/engine/joint/v4k.h @@ -18056,6 +18056,7 @@ typedef struct atlas_slice_frame_t { vec4 bounds; bool has_9slice; vec4 core; + vec2 pivot; } atlas_slice_frame_t; typedef struct atlas_slice_t { @@ -18116,13 +18117,15 @@ API void sprite_setanim(sprite_t *s, unsigned name); // game ui typedef struct guiskin_t { - void (*draw_rect_func)(void* userdata, const char *skin, vec4 rect); + void (*drawrect)(void* userdata, const char *skin, vec4 rect); + void (*getskinsize)(void* userdata, const char *skin, vec2 *size); void (*free)(void* userdata); void *userdata; } guiskin_t; API void gui_pushskin(guiskin_t skin); API void* gui_userdata(); +API vec2 gui_getskinsize(const char *skin); // -- API void gui_panel(int id, vec4 rect, const char *skin); API bool gui_button(int id, vec4 rect, const char *skin); @@ -18724,8 +18727,8 @@ API vec3 editor_pick(float mouse_x, float mouse_y); API char* editor_path(const char *path); API void editor_setmouse(int x, int y); -API vec2 editor_glyph(int x, int y, unsigned cp); -API vec2 editor_glyphstr(int x, int y, const char *utf8); +API vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); +API vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); API void editor_gizmos(int dim); // ---------------------------------------------------------------------------------------- @@ -357862,6 +357865,7 @@ static const unsigned table_middle_east[] = { static const unsigned table_emoji[] = { // 0xE000, 0xEB4C, // Private use (emojis) 0xE000, 0xF8FF, // Private use (emojis+webfonts) + 0xF0001,0xF1CC7,// Private use (icon mdi) 0 }; @@ -358049,6 +358053,7 @@ typedef struct font_t { unsigned num_glyphs; unsigned *cp2iter; unsigned *iter2cp; + unsigned begin; // first glyph. used in cp2iter table to clamp into a lesser range // font info and data int height; // bitmap height @@ -358145,7 +358150,8 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, if( font_size <= 0 || font_size > 72 ) return; if( !ttf_data || !ttf_len ) return; - flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in + if(!(flags & FONT_EM)) + flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is almost always in font_t *f = &fonts[index]; f->initialized = 1; @@ -358205,12 +358211,13 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, // pack and create bitmap unsigned char *bitmap = (unsigned char*)MALLOC(f->height*f->width); - int charCount = 0xFFFF; + int charCount = *array_back(sorted) - sorted[0] + 1; // 0xEFFFF; + f->begin = sorted[0]; f->cdata = (stbtt_packedchar*)CALLOC(1, sizeof(stbtt_packedchar) * charCount); - f->iter2cp = (unsigned*)CALLOC( 1, sizeof(unsigned) * charCount ); - for( int i = 0; i < charCount; ++i ) f->iter2cp[i] = 0xFFFD; // default invalid glyph - f->cp2iter = (unsigned*)CALLOC( 1, sizeof(unsigned) * charCount ); - for( int i = 0; i < charCount; ++i ) f->cp2iter[i] = 0xFFFD; // default invalid glyph + f->iter2cp = (unsigned*)MALLOC( sizeof(unsigned) * charCount ); + f->cp2iter = (unsigned*)MALLOC( sizeof(unsigned) * charCount ); + for( int i = 0; i < charCount; ++i ) + f->iter2cp[i] = f->cp2iter[i] = 0xFFFD; // default invalid glyph stbtt_pack_context pc; if( !stbtt_PackBegin(&pc, bitmap, f->width, f->height, 0, 1, NULL) ) { @@ -358224,11 +358231,11 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, while( i < (num-1) && (sorted[i+1]-sorted[i]) == 1 ) end = sorted[++i]; //printf("(%d,%d)", (unsigned)begin, (unsigned)end); - if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) { - for( int j = begin; j <= end; ++j ) { + if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin - f->begin) ) { + for( uint64_t cp = begin; cp <= end; ++cp ) { // unicode->index runtime lookup - f->cp2iter[ j ] = count; - f->iter2cp[ count++ ] = j; + f->cp2iter[ cp - f->begin ] = count; + f->iter2cp[ count++ ] = cp; } } else { PRINTF("!Failed to pack atlas font. Likely out of texture mem."); @@ -358260,7 +358267,7 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, for (int i = 0; i < f->num_glyphs; i++) { int cp = f->iter2cp[i]; if( cp == 0xFFFD ) continue; - stbtt_packedchar *cd = &f->cdata[ cp ]; + stbtt_packedchar *cd = &f->cdata[ cp - f->begin ]; if (cd->y1 > max_y1) { max_y1 = cd->y1; } @@ -358327,8 +358334,8 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, unsigned cp = f->iter2cp[ i ]; if(cp == 0xFFFD) continue; - stbtt_packedchar *cd = &f->cdata[ cp ]; -// if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp] = 0xFFFD; continue; } + stbtt_packedchar *cd = &f->cdata[ cp - f->begin ]; +// if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp - f->begin] = 0xFFFD; continue; } int k1 = 0*f->num_glyphs + count; int k2 = 1*f->num_glyphs + count; ++count; @@ -358532,7 +358539,7 @@ vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*draw_cm } // convert to vbo data - int cp = ch; // f->cp2iter[ch]; + int cp = ch - f->begin; // f->cp2iter[ch - f->begin]; //if(cp == 0xFFFD) continue; //if(cp > f->num_glyphs) cp = 0xFFFD; @@ -358948,6 +358955,12 @@ void *gui_userdata() { return last_skin->userdata; } +vec2 gui_getskinsize(const char *skin) { + vec2 size={0}; + if (last_skin->getskinsize) last_skin->getskinsize(last_skin->userdata, skin, &size); + return size; +} + static gui_state_t *gui_getstate(int id) { if (!ctl_states) map_init(ctl_states, less_int, hash_int); @@ -358972,13 +358985,13 @@ bool (gui_button)(int id, vec4 r, const char *skin) { } char *btn = va("%s%s", skin?skin:"button", entry->held?"_press":entry->hover?"_hover":""); - if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, btn, r); + if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, btn, r); return was_clicked; } void (gui_panel)(int id, vec4 r, const char *skin) { - if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, skin?skin:"panel", r); + if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin?skin:"panel", r); } /* skinned */ @@ -359006,15 +359019,25 @@ void skinned_draw_missing_rect(vec4 r) { static void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) { + vec4 outer = f->bounds; + r.x -= f->pivot.x*scale; + r.y -= f->pivot.y*scale; + r.z += r.x; + r.w += r.y; + + // Ensure dest rectangle is large enough to render the whole element + if ((r.z-r.x) < (outer.z-outer.x) * scale) { + r.z = r.x + (outer.z-outer.x) * scale; + } + if ((r.w-r.y) < (outer.w-outer.y) * scale) { + r.w = r.y + (outer.w-outer.y) * scale; + } + if (!f->has_9slice) { gui_drawrect(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r)); return; } - r.z += r.x; - r.w += r.y; - - vec4 outer = f->bounds; vec4 core = f->core; core.x += outer.x; core.y += outer.y; @@ -359034,14 +359057,6 @@ void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w}; vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w}; - // Ensure dest rectangle is large enough to render the whole element - if ((r.z-r.x) < (outer.z-outer.x) * scale) { - r.z = r.x + (outer.z-outer.x) * scale; - } - if ((r.w-r.y) < (outer.w-outer.y) * scale) { - r.w = r.y + (outer.w-outer.y) * scale; - } - vec4 top_left = {r.x, r.y, r.x + (core.x - outer.x) * scale, r.y + (core.y - outer.y) * scale}; vec4 top_right = {r.z - (outer.z - core.z) * scale, r.y, r.z, r.y + (core.y - outer.y) * scale}; vec4 bottom_left = {r.x, r.w - (outer.w - core.w) * scale, r.x + (core.x - outer.x) * scale, r.w}; @@ -359074,13 +359089,24 @@ void skinned_draw_rect(void* userdata, const char *skin, vec4 r) { else skinned_draw_sprite(a->scale, &a->atlas, f, r); } +void skinned_getskinsize(void *userdata, const char *skin, vec2 *size) { + skinned_t *a = C_CAST(skinned_t*, userdata); + + atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin); + if (f) { + size->x = (f->bounds.z-f->bounds.x)*a->scale; + size->y = (f->bounds.w-f->bounds.y)*a->scale; + } +} + guiskin_t gui_skinned(const char *inifile, float scale) { skinned_t *a = REALLOC(0, sizeof(skinned_t)); a->atlas = atlas_create(inifile, 0); a->scale = scale?scale:1.0f; guiskin_t skin={0}; skin.userdata = a; - skin.draw_rect_func = skinned_draw_rect; + skin.drawrect = skinned_draw_rect; + skin.getskinsize = skinned_getskinsize; skin.free = skinned_free; return skin; } @@ -371040,6 +371066,14 @@ atlas_t atlas_create(const char *inifile, unsigned flags) { a.slice_frames[index].core = vec4(x,y,x+z,y+w); } + else if ( strend(k, ".sl_pivot") ) { + array_reserve_(a.slice_frames, index); + + float x,y; + sscanf(v, "%f,%f", &x, &y); + + a.slice_frames[index].pivot = vec2(x,y); + } else if( strend(k, ".frames") ) { array_reserve_(a.anims, index); @@ -377149,27 +377183,31 @@ void editor_setmouse(int x, int y) { glfwSetCursorPos( window_handle(), x, y ); } -vec2 editor_glyph(int x, int y, unsigned cp) { +vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint) { + do_once { // style: atlas size, unicode ranges and 6 font faces max - do_once font_face(FONT_FACE2, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_2048); - do_once font_face(FONT_FACE3, "materialdesignicons-webfont.ttf", 24.f, FONT_EM|FONT_2048); // {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}}, + font_face(FONT_FACE2, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_2048); + font_face(FONT_FACE3, "materialdesignicons-webfont.ttf", 24.f, FONT_EM|FONT_2048); // {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}}, // style: 10 colors max - do_once font_color(FONT_COLOR1, WHITE); - do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY); - do_once font_color(FONT_COLOR3, YELLOW); - do_once font_color(FONT_COLOR4, ORANGE); - do_once font_color(FONT_COLOR5, CYAN); - const char *sym = codepoint_to_utf8(cp); + font_color(FONT_COLOR1, WHITE); + font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY); + font_color(FONT_COLOR3, YELLOW); + font_color(FONT_COLOR4, ORANGE); + font_color(FONT_COLOR5, CYAN); + } + font_goto(x,y); - return font_print(va("%s" FONT_H1 "%s", cp >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym)); + vec2 pos = {x,y}; + const char *sym = codepoint_to_utf8(codepoint); + return add2(pos, font_print(va("%s%s%s", style ? style : "", codepoint >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym))); } -vec2 editor_glyphstr(int x, int y, const char *utf8) { - vec2 dim = {x,y}; +vec2 editor_glyphs(int x, int y, const char *style, const char *utf8) { + vec2 pos = {x,y}; array(unsigned) codepoints = string32(utf8); for( int i = 0, end = array_count(codepoints); i < end; ++i) - add2(dim, editor_glyph(dim.x,dim.y,codepoints[i])); - return dim; + pos = add2(pos, editor_glyph(pos.x,pos.y,style,codepoints[i])); + return pos; } void editor_frame( void (*game)(unsigned, float, double) ) { diff --git a/engine/split/3rd_aseprite.h b/engine/split/3rd_aseprite.h index f892b1e..db71b10 100644 --- a/engine/split/3rd_aseprite.h +++ b/engine/split/3rd_aseprite.h @@ -1148,7 +1148,7 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct last_udata->has_text = 1; last_udata->text = s_read_string(s); } - if (flags & 2) { + if (flags & 2) { //< @zpl-zak: removed else last_udata->color.r = s_read_uint8(s); last_udata->color.g = s_read_uint8(s); last_udata->color.b = s_read_uint8(s); @@ -1178,7 +1178,8 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct slice.center_y = (int)s_read_int32(s); slice.center_w = (int)s_read_uint32(s); slice.center_h = (int)s_read_uint32(s); - } else if (flags & 2) { + } + if (flags & 2) { // Has pivot information. slice.has_pivot = 1; slice.pivot_x = (int)s_read_int32(s); diff --git a/engine/split/3rd_atlasc.h b/engine/split/3rd_atlasc.h index 16b1cb6..cac41e2 100644 --- a/engine/split/3rd_atlasc.h +++ b/engine/split/3rd_atlasc.h @@ -700,7 +700,8 @@ atlas_t* atlas_loadfiles(array(char*) files, atlas_flags flags) strcatf(&atlas_slices, "[%d].sl_9slice=%d\n", slice_idx, slice->has_center_as_9_slice); if (slice->has_center_as_9_slice) strcatf(&atlas_slices, "[%d].sl_core=%d,%d,%d,%d\n", slice_idx, slice->center_x, slice->center_y, slice->center_w, slice->center_h); - + if (slice->has_pivot) + strcatf(&atlas_slices, "[%d].sl_pivot=%d,%d\n", slice_idx, slice->pivot_x, slice->pivot_y); slice_name = slice->name; ++slice_frame_idx; } diff --git a/tools/3rd_stb_image_resize.h b/engine/split/3rd_stb_image_resize.h similarity index 100% rename from tools/3rd_stb_image_resize.h rename to engine/split/3rd_stb_image_resize.h diff --git a/tools/3rd_stb_rect_pack.h b/engine/split/3rd_stb_rect_pack.h similarity index 100% rename from tools/3rd_stb_rect_pack.h rename to engine/split/3rd_stb_rect_pack.h diff --git a/tools/3rd_tml.h b/engine/split/3rd_tml.h similarity index 100% rename from tools/3rd_tml.h rename to engine/split/3rd_tml.h diff --git a/tools/3rd_tsf.h b/engine/split/3rd_tsf.h similarity index 100% rename from tools/3rd_tsf.h rename to engine/split/3rd_tsf.h diff --git a/tools/3rd_xatlas.h b/engine/split/3rd_xatlas.h similarity index 100% rename from tools/3rd_xatlas.h rename to engine/split/3rd_xatlas.h diff --git a/engine/split/v4k_editor.c b/engine/split/v4k_editor.c index e9ab31f..6648511 100644 --- a/engine/split/v4k_editor.c +++ b/engine/split/v4k_editor.c @@ -511,27 +511,31 @@ void editor_setmouse(int x, int y) { glfwSetCursorPos( window_handle(), x, y ); } -vec2 editor_glyph(int x, int y, unsigned cp) { +vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint) { + do_once { // style: atlas size, unicode ranges and 6 font faces max - do_once font_face(FONT_FACE2, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_2048); - do_once font_face(FONT_FACE3, "materialdesignicons-webfont.ttf", 24.f, FONT_EM|FONT_2048); // {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}}, + font_face(FONT_FACE2, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_2048); + font_face(FONT_FACE3, "materialdesignicons-webfont.ttf", 24.f, FONT_EM|FONT_2048); // {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}}, // style: 10 colors max - do_once font_color(FONT_COLOR1, WHITE); - do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY); - do_once font_color(FONT_COLOR3, YELLOW); - do_once font_color(FONT_COLOR4, ORANGE); - do_once font_color(FONT_COLOR5, CYAN); - const char *sym = codepoint_to_utf8(cp); + font_color(FONT_COLOR1, WHITE); + font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY); + font_color(FONT_COLOR3, YELLOW); + font_color(FONT_COLOR4, ORANGE); + font_color(FONT_COLOR5, CYAN); + } + font_goto(x,y); - return font_print(va("%s" FONT_H1 "%s", cp >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym)); + vec2 pos = {x,y}; + const char *sym = codepoint_to_utf8(codepoint); + return add2(pos, font_print(va("%s%s%s", style ? style : "", codepoint >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym))); } -vec2 editor_glyphstr(int x, int y, const char *utf8) { - vec2 dim = {x,y}; +vec2 editor_glyphs(int x, int y, const char *style, const char *utf8) { + vec2 pos = {x,y}; array(unsigned) codepoints = string32(utf8); for( int i = 0, end = array_count(codepoints); i < end; ++i) - add2(dim, editor_glyph(dim.x,dim.y,codepoints[i])); - return dim; + pos = add2(pos, editor_glyph(pos.x,pos.y,style,codepoints[i])); + return pos; } void editor_frame( void (*game)(unsigned, float, double) ) { diff --git a/engine/split/v4k_editor.h b/engine/split/v4k_editor.h index 2602fd9..80ba437 100644 --- a/engine/split/v4k_editor.h +++ b/engine/split/v4k_editor.h @@ -90,8 +90,8 @@ API vec3 editor_pick(float mouse_x, float mouse_y); API char* editor_path(const char *path); API void editor_setmouse(int x, int y); -API vec2 editor_glyph(int x, int y, unsigned cp); -API vec2 editor_glyphstr(int x, int y, const char *utf8); +API vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); +API vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); API void editor_gizmos(int dim); // ---------------------------------------------------------------------------------------- diff --git a/engine/split/v4k_font.c b/engine/split/v4k_font.c index de1e183..f2d6c69 100644 --- a/engine/split/v4k_font.c +++ b/engine/split/v4k_font.c @@ -1388,6 +1388,7 @@ static const unsigned table_middle_east[] = { static const unsigned table_emoji[] = { // 0xE000, 0xEB4C, // Private use (emojis) 0xE000, 0xF8FF, // Private use (emojis+webfonts) + 0xF0001,0xF1CC7,// Private use (icon mdi) 0 }; @@ -1575,6 +1576,7 @@ typedef struct font_t { unsigned num_glyphs; unsigned *cp2iter; unsigned *iter2cp; + unsigned begin; // first glyph. used in cp2iter table to clamp into a lesser range // font info and data int height; // bitmap height @@ -1671,7 +1673,8 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, if( font_size <= 0 || font_size > 72 ) return; if( !ttf_data || !ttf_len ) return; - flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in + if(!(flags & FONT_EM)) + flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is almost always in font_t *f = &fonts[index]; f->initialized = 1; @@ -1731,12 +1734,13 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, // pack and create bitmap unsigned char *bitmap = (unsigned char*)MALLOC(f->height*f->width); - int charCount = 0xFFFF; + int charCount = *array_back(sorted) - sorted[0] + 1; // 0xEFFFF; + f->begin = sorted[0]; f->cdata = (stbtt_packedchar*)CALLOC(1, sizeof(stbtt_packedchar) * charCount); - f->iter2cp = (unsigned*)CALLOC( 1, sizeof(unsigned) * charCount ); - for( int i = 0; i < charCount; ++i ) f->iter2cp[i] = 0xFFFD; // default invalid glyph - f->cp2iter = (unsigned*)CALLOC( 1, sizeof(unsigned) * charCount ); - for( int i = 0; i < charCount; ++i ) f->cp2iter[i] = 0xFFFD; // default invalid glyph + f->iter2cp = (unsigned*)MALLOC( sizeof(unsigned) * charCount ); + f->cp2iter = (unsigned*)MALLOC( sizeof(unsigned) * charCount ); + for( int i = 0; i < charCount; ++i ) + f->iter2cp[i] = f->cp2iter[i] = 0xFFFD; // default invalid glyph stbtt_pack_context pc; if( !stbtt_PackBegin(&pc, bitmap, f->width, f->height, 0, 1, NULL) ) { @@ -1750,11 +1754,11 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, while( i < (num-1) && (sorted[i+1]-sorted[i]) == 1 ) end = sorted[++i]; //printf("(%d,%d)", (unsigned)begin, (unsigned)end); - if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) { - for( int j = begin; j <= end; ++j ) { + if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin - f->begin) ) { + for( uint64_t cp = begin; cp <= end; ++cp ) { // unicode->index runtime lookup - f->cp2iter[ j ] = count; - f->iter2cp[ count++ ] = j; + f->cp2iter[ cp - f->begin ] = count; + f->iter2cp[ count++ ] = cp; } } else { PRINTF("!Failed to pack atlas font. Likely out of texture mem."); @@ -1786,7 +1790,7 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, for (int i = 0; i < f->num_glyphs; i++) { int cp = f->iter2cp[i]; if( cp == 0xFFFD ) continue; - stbtt_packedchar *cd = &f->cdata[ cp ]; + stbtt_packedchar *cd = &f->cdata[ cp - f->begin ]; if (cd->y1 > max_y1) { max_y1 = cd->y1; } @@ -1853,8 +1857,8 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, unsigned cp = f->iter2cp[ i ]; if(cp == 0xFFFD) continue; - stbtt_packedchar *cd = &f->cdata[ cp ]; -// if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp] = 0xFFFD; continue; } + stbtt_packedchar *cd = &f->cdata[ cp - f->begin ]; +// if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp - f->begin] = 0xFFFD; continue; } int k1 = 0*f->num_glyphs + count; int k2 = 1*f->num_glyphs + count; ++count; @@ -2058,7 +2062,7 @@ vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*draw_cm } // convert to vbo data - int cp = ch; // f->cp2iter[ch]; + int cp = ch - f->begin; // f->cp2iter[ch - f->begin]; //if(cp == 0xFFFD) continue; //if(cp > f->num_glyphs) cp = 0xFFFD; diff --git a/engine/split/v4k_gui.c b/engine/split/v4k_gui.c index 79d1ec3..d70ce81 100644 --- a/engine/split/v4k_gui.c +++ b/engine/split/v4k_gui.c @@ -118,6 +118,12 @@ void *gui_userdata() { return last_skin->userdata; } +vec2 gui_getskinsize(const char *skin) { + vec2 size={0}; + if (last_skin->getskinsize) last_skin->getskinsize(last_skin->userdata, skin, &size); + return size; +} + static gui_state_t *gui_getstate(int id) { if (!ctl_states) map_init(ctl_states, less_int, hash_int); @@ -142,13 +148,13 @@ bool (gui_button)(int id, vec4 r, const char *skin) { } char *btn = va("%s%s", skin?skin:"button", entry->held?"_press":entry->hover?"_hover":""); - if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, btn, r); + if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, btn, r); return was_clicked; } void (gui_panel)(int id, vec4 r, const char *skin) { - if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, skin?skin:"panel", r); + if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin?skin:"panel", r); } /* skinned */ @@ -176,15 +182,25 @@ void skinned_draw_missing_rect(vec4 r) { static void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) { + vec4 outer = f->bounds; + r.x -= f->pivot.x*scale; + r.y -= f->pivot.y*scale; + r.z += r.x; + r.w += r.y; + + // Ensure dest rectangle is large enough to render the whole element + if ((r.z-r.x) < (outer.z-outer.x) * scale) { + r.z = r.x + (outer.z-outer.x) * scale; + } + if ((r.w-r.y) < (outer.w-outer.y) * scale) { + r.w = r.y + (outer.w-outer.y) * scale; + } + if (!f->has_9slice) { gui_drawrect(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r)); return; } - r.z += r.x; - r.w += r.y; - - vec4 outer = f->bounds; vec4 core = f->core; core.x += outer.x; core.y += outer.y; @@ -204,14 +220,6 @@ void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w}; vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w}; - // Ensure dest rectangle is large enough to render the whole element - if ((r.z-r.x) < (outer.z-outer.x) * scale) { - r.z = r.x + (outer.z-outer.x) * scale; - } - if ((r.w-r.y) < (outer.w-outer.y) * scale) { - r.w = r.y + (outer.w-outer.y) * scale; - } - vec4 top_left = {r.x, r.y, r.x + (core.x - outer.x) * scale, r.y + (core.y - outer.y) * scale}; vec4 top_right = {r.z - (outer.z - core.z) * scale, r.y, r.z, r.y + (core.y - outer.y) * scale}; vec4 bottom_left = {r.x, r.w - (outer.w - core.w) * scale, r.x + (core.x - outer.x) * scale, r.w}; @@ -244,13 +252,24 @@ void skinned_draw_rect(void* userdata, const char *skin, vec4 r) { else skinned_draw_sprite(a->scale, &a->atlas, f, r); } +void skinned_getskinsize(void *userdata, const char *skin, vec2 *size) { + skinned_t *a = C_CAST(skinned_t*, userdata); + + atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin); + if (f) { + size->x = (f->bounds.z-f->bounds.x)*a->scale; + size->y = (f->bounds.w-f->bounds.y)*a->scale; + } +} + guiskin_t gui_skinned(const char *inifile, float scale) { skinned_t *a = REALLOC(0, sizeof(skinned_t)); a->atlas = atlas_create(inifile, 0); a->scale = scale?scale:1.0f; guiskin_t skin={0}; skin.userdata = a; - skin.draw_rect_func = skinned_draw_rect; + skin.drawrect = skinned_draw_rect; + skin.getskinsize = skinned_getskinsize; skin.free = skinned_free; return skin; } diff --git a/engine/split/v4k_gui.h b/engine/split/v4k_gui.h index 31e3f61..a95603b 100644 --- a/engine/split/v4k_gui.h +++ b/engine/split/v4k_gui.h @@ -2,13 +2,15 @@ // game ui typedef struct guiskin_t { - void (*draw_rect_func)(void* userdata, const char *skin, vec4 rect); + void (*drawrect)(void* userdata, const char *skin, vec4 rect); + void (*getskinsize)(void* userdata, const char *skin, vec2 *size); void (*free)(void* userdata); void *userdata; } guiskin_t; API void gui_pushskin(guiskin_t skin); API void* gui_userdata(); +API vec2 gui_getskinsize(const char *skin); // -- API void gui_panel(int id, vec4 rect, const char *skin); API bool gui_button(int id, vec4 rect, const char *skin); diff --git a/engine/split/v4k_sprite.c b/engine/split/v4k_sprite.c index 7c032c6..8e57767 100644 --- a/engine/split/v4k_sprite.c +++ b/engine/split/v4k_sprite.c @@ -1277,6 +1277,14 @@ atlas_t atlas_create(const char *inifile, unsigned flags) { a.slice_frames[index].core = vec4(x,y,x+z,y+w); } + else if ( strend(k, ".sl_pivot") ) { + array_reserve_(a.slice_frames, index); + + float x,y; + sscanf(v, "%f,%f", &x, &y); + + a.slice_frames[index].pivot = vec2(x,y); + } else if( strend(k, ".frames") ) { array_reserve_(a.anims, index); diff --git a/engine/split/v4k_sprite.h b/engine/split/v4k_sprite.h index 72e4982..fa3c592 100644 --- a/engine/split/v4k_sprite.h +++ b/engine/split/v4k_sprite.h @@ -101,6 +101,7 @@ typedef struct atlas_slice_frame_t { vec4 bounds; bool has_9slice; vec4 core; + vec2 pivot; } atlas_slice_frame_t; typedef struct atlas_slice_t { diff --git a/engine/v4k.c b/engine/v4k.c index 39ba439..dbaa917 100644 --- a/engine/v4k.c +++ b/engine/v4k.c @@ -10038,6 +10038,7 @@ static const unsigned table_middle_east[] = { static const unsigned table_emoji[] = { // 0xE000, 0xEB4C, // Private use (emojis) 0xE000, 0xF8FF, // Private use (emojis+webfonts) + 0xF0001,0xF1CC7,// Private use (icon mdi) 0 }; @@ -10225,6 +10226,7 @@ typedef struct font_t { unsigned num_glyphs; unsigned *cp2iter; unsigned *iter2cp; + unsigned begin; // first glyph. used in cp2iter table to clamp into a lesser range // font info and data int height; // bitmap height @@ -10321,7 +10323,8 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, if( font_size <= 0 || font_size > 72 ) return; if( !ttf_data || !ttf_len ) return; - flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is always in + if(!(flags & FONT_EM)) + flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is almost always in font_t *f = &fonts[index]; f->initialized = 1; @@ -10381,12 +10384,13 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, // pack and create bitmap unsigned char *bitmap = (unsigned char*)MALLOC(f->height*f->width); - int charCount = 0xFFFF; + int charCount = *array_back(sorted) - sorted[0] + 1; // 0xEFFFF; + f->begin = sorted[0]; f->cdata = (stbtt_packedchar*)CALLOC(1, sizeof(stbtt_packedchar) * charCount); - f->iter2cp = (unsigned*)CALLOC( 1, sizeof(unsigned) * charCount ); - for( int i = 0; i < charCount; ++i ) f->iter2cp[i] = 0xFFFD; // default invalid glyph - f->cp2iter = (unsigned*)CALLOC( 1, sizeof(unsigned) * charCount ); - for( int i = 0; i < charCount; ++i ) f->cp2iter[i] = 0xFFFD; // default invalid glyph + f->iter2cp = (unsigned*)MALLOC( sizeof(unsigned) * charCount ); + f->cp2iter = (unsigned*)MALLOC( sizeof(unsigned) * charCount ); + for( int i = 0; i < charCount; ++i ) + f->iter2cp[i] = f->cp2iter[i] = 0xFFFD; // default invalid glyph stbtt_pack_context pc; if( !stbtt_PackBegin(&pc, bitmap, f->width, f->height, 0, 1, NULL) ) { @@ -10400,11 +10404,11 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, while( i < (num-1) && (sorted[i+1]-sorted[i]) == 1 ) end = sorted[++i]; //printf("(%d,%d)", (unsigned)begin, (unsigned)end); - if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin) ) { - for( int j = begin; j <= end; ++j ) { + if( stbtt_PackFontRange(&pc, ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar*)f->cdata + begin - f->begin) ) { + for( uint64_t cp = begin; cp <= end; ++cp ) { // unicode->index runtime lookup - f->cp2iter[ j ] = count; - f->iter2cp[ count++ ] = j; + f->cp2iter[ cp - f->begin ] = count; + f->iter2cp[ count++ ] = cp; } } else { PRINTF("!Failed to pack atlas font. Likely out of texture mem."); @@ -10436,7 +10440,7 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, for (int i = 0; i < f->num_glyphs; i++) { int cp = f->iter2cp[i]; if( cp == 0xFFFD ) continue; - stbtt_packedchar *cd = &f->cdata[ cp ]; + stbtt_packedchar *cd = &f->cdata[ cp - f->begin ]; if (cd->y1 > max_y1) { max_y1 = cd->y1; } @@ -10503,8 +10507,8 @@ void font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, unsigned cp = f->iter2cp[ i ]; if(cp == 0xFFFD) continue; - stbtt_packedchar *cd = &f->cdata[ cp ]; -// if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp] = 0xFFFD; continue; } + stbtt_packedchar *cd = &f->cdata[ cp - f->begin ]; +// if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp - f->begin] = 0xFFFD; continue; } int k1 = 0*f->num_glyphs + count; int k2 = 1*f->num_glyphs + count; ++count; @@ -10708,7 +10712,7 @@ vec2 font_draw_ex(const char *text, vec2 offset, const char *col, void (*draw_cm } // convert to vbo data - int cp = ch; // f->cp2iter[ch]; + int cp = ch - f->begin; // f->cp2iter[ch - f->begin]; //if(cp == 0xFFFD) continue; //if(cp > f->num_glyphs) cp = 0xFFFD; @@ -11124,6 +11128,12 @@ void *gui_userdata() { return last_skin->userdata; } +vec2 gui_getskinsize(const char *skin) { + vec2 size={0}; + if (last_skin->getskinsize) last_skin->getskinsize(last_skin->userdata, skin, &size); + return size; +} + static gui_state_t *gui_getstate(int id) { if (!ctl_states) map_init(ctl_states, less_int, hash_int); @@ -11148,13 +11158,13 @@ bool (gui_button)(int id, vec4 r, const char *skin) { } char *btn = va("%s%s", skin?skin:"button", entry->held?"_press":entry->hover?"_hover":""); - if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, btn, r); + if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, btn, r); return was_clicked; } void (gui_panel)(int id, vec4 r, const char *skin) { - if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, skin?skin:"panel", r); + if (last_skin->drawrect) last_skin->drawrect(last_skin->userdata, skin?skin:"panel", r); } /* skinned */ @@ -11182,15 +11192,25 @@ void skinned_draw_missing_rect(vec4 r) { static void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) { + vec4 outer = f->bounds; + r.x -= f->pivot.x*scale; + r.y -= f->pivot.y*scale; + r.z += r.x; + r.w += r.y; + + // Ensure dest rectangle is large enough to render the whole element + if ((r.z-r.x) < (outer.z-outer.x) * scale) { + r.z = r.x + (outer.z-outer.x) * scale; + } + if ((r.w-r.y) < (outer.w-outer.y) * scale) { + r.w = r.y + (outer.w-outer.y) * scale; + } + if (!f->has_9slice) { gui_drawrect(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r)); return; } - r.z += r.x; - r.w += r.y; - - vec4 outer = f->bounds; vec4 core = f->core; core.x += outer.x; core.y += outer.y; @@ -11210,14 +11230,6 @@ void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w}; vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w}; - // Ensure dest rectangle is large enough to render the whole element - if ((r.z-r.x) < (outer.z-outer.x) * scale) { - r.z = r.x + (outer.z-outer.x) * scale; - } - if ((r.w-r.y) < (outer.w-outer.y) * scale) { - r.w = r.y + (outer.w-outer.y) * scale; - } - vec4 top_left = {r.x, r.y, r.x + (core.x - outer.x) * scale, r.y + (core.y - outer.y) * scale}; vec4 top_right = {r.z - (outer.z - core.z) * scale, r.y, r.z, r.y + (core.y - outer.y) * scale}; vec4 bottom_left = {r.x, r.w - (outer.w - core.w) * scale, r.x + (core.x - outer.x) * scale, r.w}; @@ -11250,13 +11262,24 @@ void skinned_draw_rect(void* userdata, const char *skin, vec4 r) { else skinned_draw_sprite(a->scale, &a->atlas, f, r); } +void skinned_getskinsize(void *userdata, const char *skin, vec2 *size) { + skinned_t *a = C_CAST(skinned_t*, userdata); + + atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, skin); + if (f) { + size->x = (f->bounds.z-f->bounds.x)*a->scale; + size->y = (f->bounds.w-f->bounds.y)*a->scale; + } +} + guiskin_t gui_skinned(const char *inifile, float scale) { skinned_t *a = REALLOC(0, sizeof(skinned_t)); a->atlas = atlas_create(inifile, 0); a->scale = scale?scale:1.0f; guiskin_t skin={0}; skin.userdata = a; - skin.draw_rect_func = skinned_draw_rect; + skin.drawrect = skinned_draw_rect; + skin.getskinsize = skinned_getskinsize; skin.free = skinned_free; return skin; } @@ -23216,6 +23239,14 @@ atlas_t atlas_create(const char *inifile, unsigned flags) { a.slice_frames[index].core = vec4(x,y,x+z,y+w); } + else if ( strend(k, ".sl_pivot") ) { + array_reserve_(a.slice_frames, index); + + float x,y; + sscanf(v, "%f,%f", &x, &y); + + a.slice_frames[index].pivot = vec2(x,y); + } else if( strend(k, ".frames") ) { array_reserve_(a.anims, index); @@ -29325,27 +29356,31 @@ void editor_setmouse(int x, int y) { glfwSetCursorPos( window_handle(), x, y ); } -vec2 editor_glyph(int x, int y, unsigned cp) { +vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint) { + do_once { // style: atlas size, unicode ranges and 6 font faces max - do_once font_face(FONT_FACE2, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_2048); - do_once font_face(FONT_FACE3, "materialdesignicons-webfont.ttf", 24.f, FONT_EM|FONT_2048); // {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}}, + font_face(FONT_FACE2, "MaterialIconsSharp-Regular.otf", 24.f, FONT_EM|FONT_2048); + font_face(FONT_FACE3, "materialdesignicons-webfont.ttf", 24.f, FONT_EM|FONT_2048); // {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}}, // style: 10 colors max - do_once font_color(FONT_COLOR1, WHITE); - do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY); - do_once font_color(FONT_COLOR3, YELLOW); - do_once font_color(FONT_COLOR4, ORANGE); - do_once font_color(FONT_COLOR5, CYAN); - const char *sym = codepoint_to_utf8(cp); + font_color(FONT_COLOR1, WHITE); + font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY); + font_color(FONT_COLOR3, YELLOW); + font_color(FONT_COLOR4, ORANGE); + font_color(FONT_COLOR5, CYAN); + } + font_goto(x,y); - return font_print(va("%s" FONT_H1 "%s", cp >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym)); + vec2 pos = {x,y}; + const char *sym = codepoint_to_utf8(codepoint); + return add2(pos, font_print(va("%s%s%s", style ? style : "", codepoint >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym))); } -vec2 editor_glyphstr(int x, int y, const char *utf8) { - vec2 dim = {x,y}; +vec2 editor_glyphs(int x, int y, const char *style, const char *utf8) { + vec2 pos = {x,y}; array(unsigned) codepoints = string32(utf8); for( int i = 0, end = array_count(codepoints); i < end; ++i) - add2(dim, editor_glyph(dim.x,dim.y,codepoints[i])); - return dim; + pos = add2(pos, editor_glyph(pos.x,pos.y,style,codepoints[i])); + return pos; } void editor_frame( void (*game)(unsigned, float, double) ) { diff --git a/engine/v4k.h b/engine/v4k.h index 3b516bd..4be98c5 100644 --- a/engine/v4k.h +++ b/engine/v4k.h @@ -4123,6 +4123,7 @@ typedef struct atlas_slice_frame_t { vec4 bounds; bool has_9slice; vec4 core; + vec2 pivot; } atlas_slice_frame_t; typedef struct atlas_slice_t { @@ -4183,13 +4184,15 @@ API void sprite_setanim(sprite_t *s, unsigned name); // game ui typedef struct guiskin_t { - void (*draw_rect_func)(void* userdata, const char *skin, vec4 rect); + void (*drawrect)(void* userdata, const char *skin, vec4 rect); + void (*getskinsize)(void* userdata, const char *skin, vec2 *size); void (*free)(void* userdata); void *userdata; } guiskin_t; API void gui_pushskin(guiskin_t skin); API void* gui_userdata(); +API vec2 gui_getskinsize(const char *skin); // -- API void gui_panel(int id, vec4 rect, const char *skin); API bool gui_button(int id, vec4 rect, const char *skin); @@ -4791,8 +4794,8 @@ API vec3 editor_pick(float mouse_x, float mouse_y); API char* editor_path(const char *path); API void editor_setmouse(int x, int y); -API vec2 editor_glyph(int x, int y, unsigned cp); -API vec2 editor_glyphstr(int x, int y, const char *utf8); +API vec2 editor_glyph(int x, int y, const char *style, unsigned codepoint); +API vec2 editor_glyphs(int x, int y, const char *style, const char *utf8); API void editor_gizmos(int dim); // ---------------------------------------------------------------------------------------- diff --git a/tools/ase2ini.exe b/tools/ase2ini.exe index df1f458..85a2051 100644 Binary files a/tools/ase2ini.exe and b/tools/ase2ini.exe differ diff --git a/tools/ase2ini.lib b/tools/ase2ini.lib new file mode 100644 index 0000000..1c678f9 Binary files /dev/null and b/tools/ase2ini.lib differ diff --git a/tools/ase2ini.linux b/tools/ase2ini.linux index 68acc9d..961172c 100644 Binary files a/tools/ase2ini.linux and b/tools/ase2ini.linux differ diff --git a/tools/ase2ini.osx b/tools/ase2ini.osx index 38969da..c408f7c 100644 Binary files a/tools/ase2ini.osx and b/tools/ase2ini.osx differ