integrate v4k_gui
parent
5dd51bea5b
commit
6da10bec7c
90
bind/v4k.lua
90
bind/v4k.lua
|
@ -1428,6 +1428,15 @@ ffi.cdef([[
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: void ddraw_prism(vec3 center, float radius, float height, vec3 normal, int segments);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
//lcpp INF [0000] vec3: macro name but used as C declaration in: int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 position, updir, lookdir;
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 position, updir, lookdir;
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:vec2 last_look; vec3 last_move;
|
//lcpp INF [0000] vec2: macro name but used as C declaration in:vec2 last_look; vec3 last_move;
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec2 last_look; vec3 last_move;
|
//lcpp INF [0000] vec3: macro name but used as C declaration in:vec2 last_look; vec3 last_move;
|
||||||
|
@ -1510,6 +1519,13 @@ ffi.cdef([[
|
||||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:vec2 fire;
|
//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] 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] 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, gui_state_t state, const char *skin, vec4 rect);
|
||||||
|
//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);
|
||||||
|
//lcpp INF [0000] vec4: macro name but used as C declaration in:API bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
|
//lcpp INF [0000] vec4: macro name but used as C declaration in:STATIC bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
|
//lcpp INF [0000] vec4: macro name but used as C declaration in: bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
//lcpp INF [0000] test: macro name but used as C declaration in:API int (test)(const char *file, int line, const char *expr, bool result);
|
//lcpp INF [0000] test: macro name but used as C declaration in:API int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
//lcpp INF [0000] test: macro name but used as C declaration in:STATIC int (test)(const char *file, int line, const char *expr, bool result);
|
//lcpp INF [0000] test: macro name but used as C declaration in:STATIC int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
//lcpp INF [0000] test: macro name but used as C declaration in: int (test)(const char *file, int line, const char *expr, bool result);
|
//lcpp INF [0000] test: macro name but used as C declaration in: int (test)(const char *file, int line, const char *expr, bool result);
|
||||||
|
@ -1605,15 +1621,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: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: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] vec3: macro name but used as C declaration in: vec3 editor_pick(float mouse_x, float mouse_y);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
//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] vec3: macro name but used as C declaration in:API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
//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] vec3: macro name but used as C declaration in:API int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 editor_glyph(int x, int y, unsigned cp);
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
//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] vec3: macro name but used as C declaration in:STATIC int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
//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] vec3: macro name but used as C declaration in:STATIC int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
//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] vec3: macro name but used as C declaration in: int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
|
||||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
|
||||||
typedef struct FILE FILE;
|
typedef struct FILE FILE;
|
||||||
typedef long int ptrdiff_t;
|
typedef long int ptrdiff_t;
|
||||||
typedef long unsigned int size_t;
|
typedef long unsigned int size_t;
|
||||||
|
@ -3032,6 +3045,9 @@ float *pixels;
|
||||||
void ddraw_demo();
|
void ddraw_demo();
|
||||||
void ddraw_flush();
|
void ddraw_flush();
|
||||||
void ddraw_flush_projview(mat44 proj, mat44 view);
|
void ddraw_flush_projview(mat44 proj, mat44 view);
|
||||||
|
int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
||||||
|
bool gizmo_active();
|
||||||
|
bool gizmo_hover();
|
||||||
typedef struct camera_t {
|
typedef struct camera_t {
|
||||||
mat44 view, proj;
|
mat44 view, proj;
|
||||||
vec3 position, updir, lookdir;
|
vec3 position, updir, lookdir;
|
||||||
|
@ -3041,6 +3057,8 @@ float move_friction, move_damping;
|
||||||
float look_friction, look_damping;
|
float look_friction, look_damping;
|
||||||
vec2 last_look; vec3 last_move;
|
vec2 last_look; vec3 last_move;
|
||||||
bool damping;
|
bool damping;
|
||||||
|
bool orthographic;
|
||||||
|
float distance;
|
||||||
} camera_t;
|
} camera_t;
|
||||||
camera_t camera();
|
camera_t camera();
|
||||||
void camera_teleport(camera_t *cam, vec3 pos);
|
void camera_teleport(camera_t *cam, vec3 pos);
|
||||||
|
@ -3154,6 +3172,7 @@ int u_coefficients_sh;
|
||||||
char* strjoin(char** list, const char *separator);
|
char* strjoin(char** list, const char *separator);
|
||||||
char * string8(const wchar_t *str);
|
char * string8(const wchar_t *str);
|
||||||
uint32_t* string32( const char *utf8 );
|
uint32_t* string32( const char *utf8 );
|
||||||
|
const char* codepoint_to_utf8(unsigned cp);
|
||||||
unsigned intern( const char *string );
|
unsigned intern( const char *string );
|
||||||
const char *quark( unsigned key );
|
const char *quark( unsigned key );
|
||||||
typedef struct quarks_db {
|
typedef struct quarks_db {
|
||||||
|
@ -3269,7 +3288,7 @@ unsigned play;
|
||||||
bool paused;
|
bool paused;
|
||||||
struct atlas_t *a;
|
struct atlas_t *a;
|
||||||
} sprite_t;
|
} sprite_t;
|
||||||
enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3930)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3930)___COUNTER__;;
|
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__;;
|
||||||
void sprite_ctor(sprite_t *s);
|
void sprite_ctor(sprite_t *s);
|
||||||
void sprite_dtor(sprite_t *s);
|
void sprite_dtor(sprite_t *s);
|
||||||
void sprite_tick(sprite_t *s);
|
void sprite_tick(sprite_t *s);
|
||||||
|
@ -3278,10 +3297,41 @@ enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on
|
||||||
sprite_t*sprite_new(const char *ase, int bindings[6]);
|
sprite_t*sprite_new(const char *ase, int bindings[6]);
|
||||||
void sprite_del(sprite_t *s);
|
void sprite_del(sprite_t *s);
|
||||||
void sprite_setanim(sprite_t *s, unsigned name);
|
void sprite_setanim(sprite_t *s, unsigned name);
|
||||||
|
enum {
|
||||||
|
GUI_PANEL,
|
||||||
|
GUI_BUTTON,
|
||||||
|
};
|
||||||
|
typedef struct gui_state_t {
|
||||||
|
int kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool held;
|
||||||
|
bool hover;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} gui_state_t;
|
||||||
|
typedef struct guiskin_t {
|
||||||
|
void (*draw_rect_func)(void* userdata, gui_state_t state, const char *skin, vec4 rect);
|
||||||
|
void (*free)(void* userdata);
|
||||||
|
void *userdata;
|
||||||
|
} guiskin_t;
|
||||||
|
void gui_pushskin(guiskin_t skin);
|
||||||
|
void* gui_userdata();
|
||||||
|
void gui_panel(int id, vec4 rect, const char *skin);
|
||||||
|
bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
|
void gui_popskin();
|
||||||
|
guiskin_t gui_skinned(const char *inifile, float scale);
|
||||||
|
typedef struct skinned_t {
|
||||||
|
atlas_t atlas;
|
||||||
|
float scale;
|
||||||
|
char *panel;
|
||||||
|
char *button;
|
||||||
|
} skinned_t;
|
||||||
void* thread( int (*thread_func)(void* user_data), void* user_data );
|
void* thread( int (*thread_func)(void* user_data), void* user_data );
|
||||||
void thread_destroy( void *thd );
|
void thread_destroy( void *thd );
|
||||||
int argc();
|
int argc();
|
||||||
char* argv(int);
|
char* argv(int);
|
||||||
|
void argvadd(const char *arg);
|
||||||
int flag(const char *commalist);
|
int flag(const char *commalist);
|
||||||
const char* option(const char *commalist, const char *defaults);
|
const char* option(const char *commalist, const char *defaults);
|
||||||
int optioni(const char *commalist, int defaults);
|
int optioni(const char *commalist, int defaults);
|
||||||
|
@ -3620,7 +3670,7 @@ typedef struct { map base; struct { pair p; void* key; int val; } tmp, *ptr; int
|
||||||
void editor_destroy_properties(void *o);
|
void editor_destroy_properties(void *o);
|
||||||
void editor_load_on_boot(void);
|
void editor_load_on_boot(void);
|
||||||
void editor_save_on_quit(void);
|
void editor_save_on_quit(void);
|
||||||
enum {
|
enum EDITOR_MODE {
|
||||||
EDITOR_PANEL,
|
EDITOR_PANEL,
|
||||||
EDITOR_WINDOW,
|
EDITOR_WINDOW,
|
||||||
EDITOR_WINDOW_NK,
|
EDITOR_WINDOW_NK,
|
||||||
|
@ -3641,25 +3691,21 @@ EDITOR_WINDOW_NK_SMALL,
|
||||||
void editor_spawn1();
|
void editor_spawn1();
|
||||||
void editor_destroy_selected();
|
void editor_destroy_selected();
|
||||||
void editor_inspect(obj *o);
|
void editor_inspect(obj *o);
|
||||||
|
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);
|
||||||
|
void editor_gizmos(int dim);
|
||||||
int editor_send(const char *cmd);
|
int editor_send(const char *cmd);
|
||||||
const char* editor_recv(int jobid, double timeout_ss);
|
const char* editor_recv(int jobid, double timeout_ss);
|
||||||
void editor_pump();
|
void editor_pump();
|
||||||
void editor_symbol(int x, int y, const char *sym);
|
|
||||||
void editor_frame( void (*game)(unsigned, float, double) );
|
void editor_frame( void (*game)(unsigned, float, double) );
|
||||||
void editor_gizmos(int dim);
|
|
||||||
int editor_send(const char *command);
|
|
||||||
vec3 editor_pick(float mouse_x, float mouse_y);
|
|
||||||
char* editor_path(const char *path);
|
|
||||||
float* engine_getf(const char *key);
|
float* engine_getf(const char *key);
|
||||||
int* engine_geti(const char *key);
|
int* engine_geti(const char *key);
|
||||||
char** engine_gets(const char *key);
|
char** engine_gets(const char *key);
|
||||||
int engine_send(const char *cmd, const char *optional_value);
|
int engine_send(const char *cmd, const char *optional_value);
|
||||||
int ui_debug();
|
int ui_engine();
|
||||||
char* dialog_load();
|
|
||||||
char* dialog_save();
|
|
||||||
int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
|
|
||||||
bool gizmo_active();
|
|
||||||
bool gizmo_hover();
|
|
||||||
]])
|
]])
|
||||||
local _M = {}
|
local _M = {}
|
||||||
function _M.vec2(x,y)
|
function _M.vec2(x,y)
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "v4k.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
window_create(75.0, 0); // 75% size, no extra flags
|
||||||
|
|
||||||
|
font_face(FONT_FACE2, "lilita_one_regular.ttf", 32.0f, FONT_EU | FONT_2048);
|
||||||
|
|
||||||
|
gui_pushskin(gui_skinned("golden.ase", 1.0f));
|
||||||
|
atlas_t *atlas = &C_CAST(skinned_t*, gui_userdata())->atlas;
|
||||||
|
|
||||||
|
vec4 pos = vec4(100,100,350,300);
|
||||||
|
|
||||||
|
while( window_swap() && !input(KEY_ESC) ) { // game loop
|
||||||
|
if (ui_panel("Atlas", 0)) {
|
||||||
|
ui_atlas(atlas);
|
||||||
|
ui_panel_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_down(MOUSE_R)) {
|
||||||
|
pos.x = input(MOUSE_X);
|
||||||
|
pos.y = input(MOUSE_Y);
|
||||||
|
}
|
||||||
|
if (input(MOUSE_R)) {
|
||||||
|
pos.z = input(MOUSE_X);
|
||||||
|
pos.w = input(MOUSE_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (gui_button(pos, "button")) {
|
||||||
|
printf("%s\n", "Button pressed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
font_color(FONT_COLOR9, WHITE);
|
||||||
|
font_print(va(FONT_MIDDLE FONT_CENTER FONT_FACE2 FONT_COLOR9 "%s", "Hello"));
|
||||||
|
}
|
||||||
|
|
||||||
|
gui_popskin();
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,94 @@
|
||||||
|
Copyright (c) 2011 Juan Montoreano (juan@remolacha.biz),
|
||||||
|
with Reserved Font Name Lilita
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
Binary file not shown.
|
@ -0,0 +1,17 @@
|
||||||
|
uniform sampler2D texture0; /*unit0*/
|
||||||
|
uniform float u_inv_gamma;
|
||||||
|
uniform vec4 u_tint;
|
||||||
|
uniform int u_has_tex;
|
||||||
|
|
||||||
|
in vec2 uv;
|
||||||
|
out vec4 fragcolor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 texel = texture( texture0, uv );
|
||||||
|
if (u_has_tex > 0) {
|
||||||
|
fragcolor = vec4(texel.xyz*u_tint.xyz, texel.a*u_tint.a);
|
||||||
|
} else {
|
||||||
|
fragcolor = u_tint;
|
||||||
|
}
|
||||||
|
fragcolor.rgb = pow( fragcolor.rgb, vec3( u_inv_gamma ) ); // defaults: 1.0/2.2 gamma
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 aPos;
|
||||||
|
layout (location = 1) in vec2 aTexCoord;
|
||||||
|
uniform float u_window_width;
|
||||||
|
uniform float u_window_height;
|
||||||
|
out vec2 uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);
|
||||||
|
// float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);
|
||||||
|
float px = (aPos.x / u_window_width) * 2.0 - 1.0;
|
||||||
|
float py = 1.0 - (aPos.y / u_window_height) * 2.0;
|
||||||
|
gl_Position = vec4(px, py, 0.0, 1.0);
|
||||||
|
// gl_Position = vec4(-1.0 + x*2.0, 0.0-(-1.0+y*2.0), 0.0, 1.0); // normal(0+),flipped(0-)
|
||||||
|
// uv = vec2(x, y); // normal(y),flipped(1.0-y)
|
||||||
|
uv = aTexCoord; // normal(y),flipped(1.0-y)
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
#include "v4k.h"
|
#include "v4k.h"
|
||||||
#include "split/3rd_icon_mdi.h"
|
#include "split/3rd_icon_mdi.h"
|
||||||
|
|
||||||
|
#define SWAP(T,a,b) do { T c = (a); (a) = (b); (b) = c; } while(0)
|
||||||
|
|
||||||
#if 0 // v4k_pack proposal
|
#if 0 // v4k_pack proposal
|
||||||
static __thread char* mpin;
|
static __thread char* mpin;
|
||||||
static __thread unsigned mpinlen;
|
static __thread unsigned mpinlen;
|
||||||
|
@ -127,15 +129,6 @@ right floating icons can be dragged: cam orientation, cam panning, cam zooming,
|
||||||
ctrl-z undo, ctrl-shift-z redo
|
ctrl-z undo, ctrl-shift-z redo
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char* codepoint_to_utf8_(unsigned c) { //< @r-lyeh
|
|
||||||
static char s[4+1];
|
|
||||||
memset(s, 0, 5);
|
|
||||||
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
|
||||||
else if (c < 0x800) s[0] = 0xC0 | ((c >> 6) & 0x1F), s[1] = 0x80 | ( c & 0x3F), s[2] = 0;
|
|
||||||
else if (c < 0x10000) s[0] = 0xE0 | ((c >> 12) & 0x0F), s[1] = 0x80 | ((c >> 6) & 0x3F), s[2] = 0x80 | ( c & 0x3F), s[3] = 0;
|
|
||||||
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
bool is_hovering(vec4 rect, vec2 mouse) { // rect(x,y,x2,y2)
|
bool is_hovering(vec4 rect, vec2 mouse) { // rect(x,y,x2,y2)
|
||||||
if( mouse.x < rect.x ) return 0;
|
if( mouse.x < rect.x ) return 0;
|
||||||
if( mouse.x > rect.z ) return 0;
|
if( mouse.x > rect.z ) return 0;
|
||||||
|
@ -169,8 +162,8 @@ int editor_toolbar(int x, int y, int incw, int inch, const char *sym) {
|
||||||
for each_array(codepoints, uint32_t, g) {
|
for each_array(codepoints, uint32_t, g) {
|
||||||
int selected = oo ? is_hovering(vec4(ix,iy,ix+inc,iy+inc),vec2(ox,oy)) : 0;
|
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));
|
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));
|
const char *str8 = va("%s%s", selected || hovering ? FONT_COLOR1 : FONT_COLOR2, codepoint_to_utf8(g));
|
||||||
editor_symbol(ix + inc/8, iy + inc/3 + 2, str8);
|
editor_glyphstr(ix + inc/8, iy + inc/3 + 2, str8);
|
||||||
ix += incw;
|
ix += incw;
|
||||||
iy += inch;
|
iy += inch;
|
||||||
}
|
}
|
||||||
|
@ -191,8 +184,8 @@ int editor_toolbar(int x, int y, int incw, int inch, const char *sym) {
|
||||||
int mcx = ((ox - x) / inc) + 1, mcy = ((oy - y) / inc) + 1; // mouse cells
|
int mcx = ((ox - x) / inc) + 1, mcy = ((oy - y) / inc) + 1; // mouse cells
|
||||||
editor_toolbar_drag.x = mx - editor_toolbar_drag.z;
|
editor_toolbar_drag.x = mx - editor_toolbar_drag.z;
|
||||||
editor_toolbar_drag.y = my - editor_toolbar_drag.w;
|
editor_toolbar_drag.y = my - editor_toolbar_drag.w;
|
||||||
API void editor_cursorpos(int x, int y);
|
API void editor_setmouse(int x, int y);
|
||||||
editor_cursorpos(ox, oy);
|
editor_setmouse(ox, oy);
|
||||||
editor_toolbar_drag.z = ox;
|
editor_toolbar_drag.z = ox;
|
||||||
editor_toolbar_drag.w = oy;
|
editor_toolbar_drag.w = oy;
|
||||||
return incw ? -mcx : -mcy;
|
return incw ? -mcx : -mcy;
|
||||||
|
@ -258,13 +251,13 @@ int lit_edit(lit *obj) {
|
||||||
ICON_MDI_WEATHER_SUNNY // directional
|
ICON_MDI_WEATHER_SUNNY // directional
|
||||||
ICON_MDI_LIGHTBULB_FLUORESCENT_TUBE_OUTLINE
|
ICON_MDI_LIGHTBULB_FLUORESCENT_TUBE_OUTLINE
|
||||||
;
|
;
|
||||||
// editor_symbol(obj->pos.x+16,obj->pos.y-32,all_icons);
|
// editor_glyphstr(obj->pos.x+16,obj->pos.y-32,all_icons);
|
||||||
if( editor_selected(obj) ) {
|
if( editor_selected(obj) ) {
|
||||||
obj->pos.x += input(KEY_RIGHT) - input(KEY_LEFT);
|
obj->pos.x += input(KEY_RIGHT) - input(KEY_LEFT);
|
||||||
obj->pos.y += input(KEY_DOWN) - input(KEY_UP);
|
obj->pos.y += input(KEY_DOWN) - input(KEY_UP);
|
||||||
obj->type = (obj->type + !!input_down(KEY_SPACE)) % 4;
|
obj->type = (obj->type + !!input_down(KEY_SPACE)) % 4;
|
||||||
}
|
}
|
||||||
editor_symbol(obj->pos.x,obj->pos.y,lit_icon(obj));
|
editor_glyphstr(obj->pos.x,obj->pos.y,lit_icon(obj));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,7 +331,7 @@ int kid_edit(kid *obj) {
|
||||||
obj->pos.x += input(KEY_RIGHT) - input(KEY_LEFT);
|
obj->pos.x += input(KEY_RIGHT) - input(KEY_LEFT);
|
||||||
obj->pos.y += input(KEY_DOWN) - input(KEY_UP);
|
obj->pos.y += input(KEY_DOWN) - input(KEY_UP);
|
||||||
|
|
||||||
editor_symbol(obj->pos.x+16,obj->pos.y-16,ICON_MD_VIDEOGAME_ASSET);
|
editor_glyphstr(obj->pos.x+16,obj->pos.y-16,ICON_MD_VIDEOGAME_ASSET);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -470,15 +463,21 @@ int main(){
|
||||||
// https://github.com/glfw/glfw/pull/990
|
// https://github.com/glfw/glfw/pull/990
|
||||||
|
|
||||||
window_title("Editor " EDITOR_VERSION);
|
window_title("Editor " EDITOR_VERSION);
|
||||||
window_create( flag("--transparent") ? 101 : 80, flag("--windowed") ? 0 : WINDOW_BORDERLESS);
|
window_create( flag("--transparent") ? 101 : 80, WINDOW_MSAA4 | (flag("--windowed") ? 0 : WINDOW_BORDERLESS));
|
||||||
window_icon("scale-ruler-icon.png");
|
window_icon("scale-ruler-icon.png");
|
||||||
|
|
||||||
while( window_swap() ) {
|
while( window_swap() ) {
|
||||||
editor_frame(game);
|
editor_frame(game);
|
||||||
editor_gizmos(2);
|
editor_gizmos(2);
|
||||||
|
|
||||||
int choice1 = editor_toolbar(window_width()-32, ui_has_menubar() ? 34 : 0, 0, 32, ICON_MD_VISIBILITY ICON_MD_360 ICON_MD_ZOOM_OUT_MAP ICON_MD_GRID_ON ); // ICON_MDI_ORBIT ICON_MDI_LOUPE ICON_MDI_GRID );
|
camera_t *cam = camera_get_active();
|
||||||
//int choice2 = editor_toolbar(window_width()-32*2, ui_has_menubar() ? 34 : 0, -32, 0, ICON_MD_360 ICON_MD_ZOOM_OUT_MAP ICON_MD_GRID_ON ); // ICON_MDI_ORBIT ICON_MDI_LOUPE ICON_MDI_GRID );
|
|
||||||
|
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 );
|
||||||
|
int choice2 = editor_toolbar(window_width()-32*2, ui_has_menubar() ? 34 : 0, -32, 0, ICON_MD_SQUARE_FOOT );
|
||||||
|
|
||||||
if( choice1 > 0 ) { // clicked[>0]
|
if( choice1 > 0 ) { // clicked[>0]
|
||||||
camera_t *cam = camera_get_active();
|
camera_t *cam = camera_get_active();
|
||||||
|
@ -487,10 +486,12 @@ int main(){
|
||||||
if( choice1 < 0 ) { // dragged[<0]
|
if( choice1 < 0 ) { // dragged[<0]
|
||||||
vec2 mouse_sensitivity = vec2(0.1, -0.1); // sensitivity + polarity
|
vec2 mouse_sensitivity = vec2(0.1, -0.1); // sensitivity + polarity
|
||||||
vec2 drag = mul2( editor_toolbar_dragged(), mouse_sensitivity );
|
vec2 drag = mul2( editor_toolbar_dragged(), mouse_sensitivity );
|
||||||
camera_t *cam = camera_get_active();
|
|
||||||
if( choice1 == -1 ) camera_fps(cam, drag.x, drag.y );
|
if( choice1 == -1 ) camera_fps(cam, drag.x, drag.y );
|
||||||
if( choice1 == -2 ) camera_orbit(cam, drag.x, drag.y, 0); //len3(cam->position) );
|
if( choice1 == -2 ) camera_orbit(cam, drag.x, drag.y, 0); //len3(cam->position) );
|
||||||
if( choice1 == -3 ) camera_fov(cam, cam->fov += drag.y - drag.x);
|
if( choice1 == -3 ) camera_fov(cam, cam->fov += drag.y - drag.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// font demo
|
||||||
|
font_print(va(FONT_BOTTOM FONT_RIGHT FONT_H6 "(CAM: %5.2f,%5.2f,%5.2f)", cam->position.x, cam->position.y, cam->position.z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
#ifndef ICON_MD_H
|
#ifndef ICON_MD_H
|
||||||
#define ICON_MD_H
|
#define ICON_MD_H
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MD "MaterialIcons-Regular.ttf"
|
#define ICON_MD_FILENAME "MaterialIcons-Regular.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MD 0xe000
|
#define ICON_MD_MIN 0xe000
|
||||||
#define ICON_MAX_16_MD 0xf8ff
|
#define ICON_MD_MAX_16 0xf8ff
|
||||||
#define ICON_MAX_MD 0x10fffd
|
#define ICON_MD_MAX 0x10fffd
|
||||||
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
||||||
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
||||||
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
||||||
|
@ -17917,6 +17917,8 @@ API char* strjoin(array(char*) list, const char *separator);
|
||||||
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
||||||
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
||||||
|
|
||||||
|
API const char* codepoint_to_utf8(unsigned cp);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ## string interning (quarks)
|
// ## string interning (quarks)
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
|
@ -18108,6 +18110,57 @@ API void sprite_edit(sprite_t *s);
|
||||||
API sprite_t*sprite_new(const char *ase, int bindings[6]);
|
API sprite_t*sprite_new(const char *ase, int bindings[6]);
|
||||||
API void sprite_del(sprite_t *s);
|
API void sprite_del(sprite_t *s);
|
||||||
API void sprite_setanim(sprite_t *s, unsigned name);
|
API void sprite_setanim(sprite_t *s, unsigned name);
|
||||||
|
#line 0
|
||||||
|
#line 1 "v4k_gui.h"
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GUI_PANEL,
|
||||||
|
GUI_BUTTON,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct gui_state_t {
|
||||||
|
int kind;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool held;
|
||||||
|
bool hover;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} gui_state_t;
|
||||||
|
|
||||||
|
typedef struct guiskin_t {
|
||||||
|
void (*draw_rect_func)(void* userdata, gui_state_t state, const char *skin, vec4 rect);
|
||||||
|
void (*free)(void* userdata);
|
||||||
|
void *userdata;
|
||||||
|
} guiskin_t;
|
||||||
|
|
||||||
|
API void gui_pushskin(guiskin_t skin);
|
||||||
|
API void* gui_userdata();
|
||||||
|
// --
|
||||||
|
API void gui_panel(int id, vec4 rect, const char *skin);
|
||||||
|
API bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
|
API void gui_popskin();
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
#define gui_panel(...) gui_panel(__LINE__, __VA_ARGS__)
|
||||||
|
#define gui_button(...) gui_button(__LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
|
// default skins
|
||||||
|
API guiskin_t gui_skinned(const char *inifile, float scale);
|
||||||
|
|
||||||
|
typedef struct skinned_t {
|
||||||
|
atlas_t atlas;
|
||||||
|
float scale;
|
||||||
|
// unsigned framenum;
|
||||||
|
|
||||||
|
//skins
|
||||||
|
char *panel;
|
||||||
|
char *button;
|
||||||
|
} skinned_t;
|
||||||
|
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_system.h"
|
#line 1 "v4k_system.h"
|
||||||
|
@ -18690,7 +18743,9 @@ API void editor_inspect(obj *o);
|
||||||
API vec3 editor_pick(float mouse_x, float mouse_y);
|
API vec3 editor_pick(float mouse_x, float mouse_y);
|
||||||
API char* editor_path(const char *path);
|
API char* editor_path(const char *path);
|
||||||
|
|
||||||
API void editor_symbol(int x, int y, const char *sym);
|
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 void editor_gizmos(int dim);
|
API void editor_gizmos(int dim);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
@ -30438,11 +30493,11 @@ int gladLoadGL( GLADloadfunc load) {
|
||||||
#ifndef ICON_MD_H
|
#ifndef ICON_MD_H
|
||||||
#define ICON_MD_H
|
#define ICON_MD_H
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MD "MaterialIcons-Regular.ttf"
|
#define ICON_MD_FILENAME "MaterialIcons-Regular.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MD 0xe000
|
#define ICON_MD_MIN 0xe000
|
||||||
#define ICON_MAX_16_MD 0xf8ff
|
#define ICON_MD_MAX_16 0xf8ff
|
||||||
#define ICON_MAX_MD 0x10fffd
|
#define ICON_MD_MAX 0x10fffd
|
||||||
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
||||||
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
||||||
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
||||||
|
@ -338550,11 +338605,11 @@ rpmalloc_linker_reference(void) {
|
||||||
// for use with https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf
|
// for use with https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MDI "materialdesignicons-webfont.ttf"
|
#define ICON_MDI_FILENAME "materialdesignicons-webfont.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MDI 0xF68C
|
#define ICON_MDI_MIN 0xF68C
|
||||||
#define ICON_MAX_16_MDI 0xF68C
|
#define ICON_MDI_MAX_16 0xF68C
|
||||||
#define ICON_MAX_MDI 0xF1CC7
|
#define ICON_MDI_MAX 0xF1CC7
|
||||||
#define ICON_MDI_AB_TESTING "\xf3\xb0\x87\x89" // U+F01C9
|
#define ICON_MDI_AB_TESTING "\xf3\xb0\x87\x89" // U+F01C9
|
||||||
#define ICON_MDI_ABACUS "\xf3\xb1\x9b\xa0" // U+F16E0
|
#define ICON_MDI_ABACUS "\xf3\xb1\x9b\xa0" // U+F16E0
|
||||||
#define ICON_MDI_ABJAD_ARABIC "\xf3\xb1\x8c\xa8" // U+F1328
|
#define ICON_MDI_ABJAD_ARABIC "\xf3\xb1\x8c\xa8" // U+F1328
|
||||||
|
@ -346562,7 +346617,7 @@ int printi(int i) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* codepoint_to_utf8(unsigned c);
|
static const char* codepoint_to_utf8_(unsigned c);
|
||||||
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char buf[16];
|
char buf[16];
|
||||||
|
@ -346606,7 +346661,7 @@ int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved
|
||||||
goto bottom;
|
goto bottom;
|
||||||
|
|
||||||
break; case GLEQ_CODEPOINT_INPUT:
|
break; case GLEQ_CODEPOINT_INPUT:
|
||||||
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8(e.codepoint));
|
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
|
||||||
|
|
||||||
break; case GLEQ_BUTTON_PRESSED:
|
break; case GLEQ_BUTTON_PRESSED:
|
||||||
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
||||||
|
@ -346758,7 +346813,7 @@ struct RenFont {
|
||||||
|
|
||||||
static struct { int left, top, right, bottom; } lt_clip;
|
static struct { int left, top, right, bottom; } lt_clip;
|
||||||
|
|
||||||
static const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
static const char* codepoint_to_utf8_(unsigned c) { //< @r-lyeh
|
||||||
static char s[4+1];
|
static char s[4+1];
|
||||||
lt_memset(s, 0, 5);
|
lt_memset(s, 0, 5);
|
||||||
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
||||||
|
@ -346767,7 +346822,7 @@ static const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
||||||
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
static const char* utf8_to_codepoint_(const char *p, unsigned *dst) {
|
||||||
unsigned res, n;
|
unsigned res, n;
|
||||||
switch (*p & 0xf0) {
|
switch (*p & 0xf0) {
|
||||||
case 0xf0 : res = *p & 0x07; n = 3; break;
|
case 0xf0 : res = *p & 0x07; n = 3; break;
|
||||||
|
@ -346943,7 +346998,7 @@ int ren_get_font_width(RenFont *font, const char *text) {
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint_(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSet *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
||||||
x += g->xadvance;
|
x += g->xadvance;
|
||||||
|
@ -347048,7 +347103,7 @@ int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color)
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint_(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSet *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
||||||
rect.x = g->x0;
|
rect.x = g->x0;
|
||||||
|
@ -348635,6 +348690,16 @@ array(uint32_t) string32( const char *utf8 ) {
|
||||||
return out[slot];
|
return out[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
||||||
|
static char s[4+1];
|
||||||
|
memset(s, 0, 5);
|
||||||
|
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
||||||
|
else if (c < 0x800) s[0] = 0xC0 | ((c >> 6) & 0x1F), s[1] = 0x80 | ( c & 0x3F), s[2] = 0;
|
||||||
|
else if (c < 0x10000) s[0] = 0xE0 | ((c >> 12) & 0x0F), s[1] = 0x80 | ((c >> 6) & 0x3F), s[2] = 0x80 | ( c & 0x3F), s[3] = 0;
|
||||||
|
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// quarks
|
// quarks
|
||||||
|
|
||||||
|
@ -349157,9 +349222,9 @@ void* ui_handle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nk_config_custom_fonts() {
|
static void nk_config_custom_fonts() {
|
||||||
#define UI_ICON_MIN ICON_MIN_MD
|
#define UI_ICON_MIN ICON_MD_MIN
|
||||||
#define UI_ICON_MED ICON_MAX_16_MD
|
#define UI_ICON_MED ICON_MD_MAX_16
|
||||||
#define UI_ICON_MAX ICON_MAX_MD
|
#define UI_ICON_MAX ICON_MD_MAX
|
||||||
|
|
||||||
#define ICON_BARS ICON_MD_MENU
|
#define ICON_BARS ICON_MD_MENU
|
||||||
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
||||||
|
@ -349190,7 +349255,7 @@ static void nk_config_custom_fonts() {
|
||||||
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
||||||
} icons[] = {
|
} icons[] = {
|
||||||
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
||||||
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MIN_MDI*/, 0xF1CC7/*ICON_MAX_MDI*/, 0}},
|
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}},
|
||||||
};
|
};
|
||||||
for( int f = 0; f < countof(icons); ++f )
|
for( int f = 0; f < countof(icons); ++f )
|
||||||
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
||||||
|
@ -358782,6 +358847,306 @@ vec2 font_rect(const char *str) {
|
||||||
}
|
}
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
|
#line 1 "v4k_gui.c"
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui (utils)
|
||||||
|
|
||||||
|
API vec2i draw_window_ui();
|
||||||
|
|
||||||
|
API void draw_rect(int rgba, vec2 start, vec2 end );
|
||||||
|
API void draw_rect_tex( texture_t texture, int rgba, vec2 start, vec2 end );
|
||||||
|
API void draw_rect_sheet( texture_t spritesheet, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end );
|
||||||
|
|
||||||
|
#define draw_rect_borders(color, x, y, w, h, borderWeight) do { \
|
||||||
|
int x1 = (x); \
|
||||||
|
int y1 = (y); \
|
||||||
|
int x2 = (x) + (w) - 1; \
|
||||||
|
int y2 = (y) + (h) - 1; \
|
||||||
|
draw_rect(color, vec2(x1, y1), vec2(x2, y1 + (borderWeight) - 1)); \
|
||||||
|
draw_rect(color, vec2(x1, y1), vec2(x1 + (borderWeight) - 1, y2)); \
|
||||||
|
draw_rect(color, vec2(x1, y2 - (borderWeight) + 1), vec2(x2, y2)); \
|
||||||
|
draw_rect(color, vec2(x2 - (borderWeight) + 1, y1), vec2(x2, y2)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
// #define lay_draw_rect(rgba, rect) draw_rect(rgba, vec2(rect.e[0], rect.e[1]), vec2(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
// #define lay_draw_rect_borders(rgba, rect, borderWeight) draw_rect_borders(rgba, rect.e[0], rect.e[1], rect.e[2], rect.e[3], borderWeight)
|
||||||
|
// #define lay_draw_rect_tex(tex, rgba, rect) draw_rect_tex(tex, rgba, vec2(rect.e[0], rect.e[1]), vec2(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
// #define l2m(rect) (vec4(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
#define v42v2(rect) vec2(rect.x,rect.y), vec2(rect.z,rect.w)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vec2i draw_window_ui() {
|
||||||
|
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||||
|
int w = window_width();
|
||||||
|
int h = window_height();
|
||||||
|
return vec2i(w/dpi.x, h/dpi.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect_sheet( texture_t texture, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end ) {
|
||||||
|
float gamma = 1;
|
||||||
|
static int program = -1, vbo = -1, vao = -1, u_inv_gamma = -1, u_tint = -1, u_has_tex = -1, u_window_width = -1, u_window_height = -1;
|
||||||
|
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||||
|
if( program < 0 ) {
|
||||||
|
const char* vs = vfs_read("shaders/rect_2d.vs");
|
||||||
|
const char* fs = vfs_read("shaders/rect_2d.fs");
|
||||||
|
|
||||||
|
program = shader(vs, fs, "", "fragcolor" , NULL);
|
||||||
|
ASSERT(program > 0);
|
||||||
|
u_inv_gamma = glGetUniformLocation(program, "u_inv_gamma");
|
||||||
|
u_tint = glGetUniformLocation(program, "u_tint");
|
||||||
|
u_has_tex = glGetUniformLocation(program, "u_has_tex");
|
||||||
|
u_window_width = glGetUniformLocation(program, "u_window_width");
|
||||||
|
u_window_height = glGetUniformLocation(program, "u_window_height");
|
||||||
|
glGenVertexArrays( 1, (GLuint*)&vao );
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = mul2(start, dpi);
|
||||||
|
end = mul2(end, dpi);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendEquation(GL_FUNC_ADD);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
GLenum texture_type = texture.flags & TEXTURE_ARRAY ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
||||||
|
// glEnable( GL_BLEND );
|
||||||
|
glUseProgram( program );
|
||||||
|
glUniform1f( u_inv_gamma, 1.0f / (gamma + !gamma) );
|
||||||
|
|
||||||
|
glBindVertexArray( vao );
|
||||||
|
|
||||||
|
glActiveTexture( GL_TEXTURE0 );
|
||||||
|
glBindTexture( texture_type, texture.id );
|
||||||
|
|
||||||
|
glUniform1i(u_has_tex, (texture.id != 0));
|
||||||
|
glUniform1f(u_window_width, (float)window_width());
|
||||||
|
glUniform1f(u_window_height, (float)window_height());
|
||||||
|
|
||||||
|
vec4 rgbaf = {((rgba>>24)&255)/255.f, ((rgba>>16)&255)/255.f,((rgba>>8)&255)/255.f,((rgba>>0)&255)/255.f};
|
||||||
|
glUniform4fv(u_tint, GL_TRUE, &rgbaf.x);
|
||||||
|
|
||||||
|
// normalize texture regions
|
||||||
|
if (texture.id != 0) {
|
||||||
|
tex_start.x /= texture.w;
|
||||||
|
tex_start.y /= texture.h;
|
||||||
|
tex_end.x /= texture.w;
|
||||||
|
tex_end.y /= texture.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLfloat vertices[] = {
|
||||||
|
// Positions // UVs
|
||||||
|
start.x, start.y, tex_start.x, tex_start.y,
|
||||||
|
end.x, start.y, tex_end.x, tex_start.y,
|
||||||
|
end.x, end.y, tex_end.x, tex_end.y,
|
||||||
|
start.x, start.y, tex_start.x, tex_start.y,
|
||||||
|
end.x, end.y, tex_end.x, tex_end.y,
|
||||||
|
start.x, end.y, tex_start.x, tex_end.y
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
|
||||||
|
|
||||||
|
glDrawArrays( GL_TRIANGLES, 0, 6 );
|
||||||
|
profile_incstat("Render.num_drawcalls", +1);
|
||||||
|
profile_incstat("Render.num_triangles", +2);
|
||||||
|
|
||||||
|
glBindTexture( texture_type, 0 );
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
glDisableVertexAttribArray(1);
|
||||||
|
glBindVertexArray( 0 );
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
glUseProgram( 0 );
|
||||||
|
// glDisable( GL_BLEND );
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect_tex( texture_t texture, int rgba, vec2 start, vec2 end ) {
|
||||||
|
draw_rect_sheet(texture, vec2(0, 0), vec2(texture.w, texture.h), rgba, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect(int rgba, vec2 start, vec2 end ) {
|
||||||
|
draw_rect_tex((texture_t){0}, rgba, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui
|
||||||
|
|
||||||
|
static __thread array(guiskin_t) skins=0;
|
||||||
|
static __thread guiskin_t *last_skin=0;
|
||||||
|
static __thread map(int, gui_state_t) ctl_states=0; //@leak
|
||||||
|
|
||||||
|
void gui_pushskin(guiskin_t skin) {
|
||||||
|
array_push(skins, skin);
|
||||||
|
last_skin = array_back(skins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_popskin() {
|
||||||
|
if (!last_skin) return;
|
||||||
|
if (last_skin->free) last_skin->free(last_skin->userdata);
|
||||||
|
array_pop(skins);
|
||||||
|
last_skin = array_count(skins) ? array_back(skins) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *gui_userdata() {
|
||||||
|
return last_skin->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
gui_state_t *gui_getstate(int id, int kind) {
|
||||||
|
if (!ctl_states) map_init(ctl_states, less_int, hash_int);
|
||||||
|
static gui_state_t st={0};
|
||||||
|
st.kind=kind;
|
||||||
|
return map_find_or_add(ctl_states, id, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool (gui_button)(int id, vec4 r, const char *skin) {
|
||||||
|
gui_state_t *entry = gui_getstate(id, GUI_BUTTON);
|
||||||
|
bool was_clicked=0;
|
||||||
|
entry->hover = false;
|
||||||
|
|
||||||
|
if (input(MOUSE_X) > r.x && input(MOUSE_X) < r.z && input(MOUSE_Y) > r.y && input(MOUSE_Y) < r.w) {
|
||||||
|
if (input_up(MOUSE_L) && entry->held) {
|
||||||
|
was_clicked=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->held = input_held(MOUSE_L);
|
||||||
|
entry->hover = true;
|
||||||
|
}
|
||||||
|
else if (input_up(MOUSE_L) && entry->held) {
|
||||||
|
entry->held = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, *entry, skin, r);
|
||||||
|
else {
|
||||||
|
draw_rect(entry->held ? 0x111111FF : entry->hover ? 0xEEEEEEFF : 0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return was_clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (gui_panel)(int id, vec4 r, const char *skin) {
|
||||||
|
gui_state_t *entry = gui_getstate(id, GUI_PANEL);
|
||||||
|
if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, *entry, skin?skin:"panel", r);
|
||||||
|
else {
|
||||||
|
draw_rect(0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skinned */
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_free(void* userdata) {
|
||||||
|
skinned_t *a = C_CAST(skinned_t*, userdata);
|
||||||
|
atlas_destroy(&a->atlas);
|
||||||
|
FREE(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
atlas_slice_frame_t *skinned_getsliceframe(atlas_t *a, const char *name, const char *fallback) {
|
||||||
|
#define atlas_loop(n)\
|
||||||
|
for (int i = 0; i < array_count(a->slices); i++)\
|
||||||
|
if (!strcmp(quark_string(&a->db, a->slices[i].name), n))\
|
||||||
|
return &a->slice_frames[a->slices[i].frames[0]];
|
||||||
|
atlas_loop(name);
|
||||||
|
atlas_loop(fallback);
|
||||||
|
return NULL;
|
||||||
|
#undef atlas_loop
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_missing_rect(vec4 r) {
|
||||||
|
draw_rect_tex(texture_checker(), 0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) {
|
||||||
|
if (!f->has_9slice) {
|
||||||
|
draw_rect_sheet(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 outer = f->bounds;
|
||||||
|
vec4 core = f->core;
|
||||||
|
core.x += outer.x;
|
||||||
|
core.y += outer.y;
|
||||||
|
core.z += outer.x;
|
||||||
|
core.w += outer.y;
|
||||||
|
|
||||||
|
// Define the 9 slices
|
||||||
|
vec4 top_left_slice = {outer.x, outer.y, core.x, core.y};
|
||||||
|
vec4 top_middle_slice = {core.x, outer.y, core.z, core.y};
|
||||||
|
vec4 top_right_slice = {core.z, outer.y, outer.z, core.y};
|
||||||
|
|
||||||
|
vec4 middle_left_slice = {outer.x, core.y, core.x, core.w};
|
||||||
|
vec4 center_slice = core;
|
||||||
|
vec4 middle_right_slice = {core.z, core.y, outer.z, core.w};
|
||||||
|
|
||||||
|
vec4 bottom_left_slice = {outer.x, core.w, core.x, outer.w};
|
||||||
|
vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w};
|
||||||
|
vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w};
|
||||||
|
|
||||||
|
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};
|
||||||
|
vec4 bottom_right = {r.z - (outer.z - core.z) * scale, r.w - (outer.w - core.w) * scale, r.z, r.w};
|
||||||
|
|
||||||
|
vec4 top = {top_left.z, r.y, top_right.x, top_left.w};
|
||||||
|
vec4 bottom = {bottom_left.z, bottom_left.y, bottom_right.x, r.w};
|
||||||
|
vec4 left = {r.x, top_left.w, top_left.z, bottom_left.y};
|
||||||
|
vec4 right = {top_right.x, top_right.w, r.z, bottom_right.y};
|
||||||
|
|
||||||
|
vec4 center = {top_left.z, top_left.w, top_right.x, bottom_right.y};
|
||||||
|
|
||||||
|
draw_rect_sheet(a->tex, v42v2(center_slice), 0xFFFFFFFF, v42v2(center));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_left_slice), 0xFFFFFFFF, v42v2(top_left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_right_slice), 0xFFFFFFFF, v42v2(top_right));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_left_slice), 0xFFFFFFFF, v42v2(bottom_left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_right_slice), 0xFFFFFFFF, v42v2(bottom_right));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_middle_slice), 0xFFFFFFFF, v42v2(top));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_middle_slice), 0xFFFFFFFF, v42v2(bottom));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(middle_left_slice), 0xFFFFFFFF, v42v2(left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(middle_right_slice), 0xFFFFFFFF, v42v2(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_rect(void* userdata, gui_state_t state, const char *skin, vec4 r) {
|
||||||
|
skinned_t *a = C_CAST(skinned_t*, userdata);
|
||||||
|
|
||||||
|
switch (state.kind) {
|
||||||
|
case GUI_BUTTON: {
|
||||||
|
char *btn = va("%s%s", skin?skin:a->button, state.held?"_press":state.hover?"_hover":"");
|
||||||
|
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, btn, skin?skin:a->button);
|
||||||
|
if (!f) skinned_draw_missing_rect(r);
|
||||||
|
else skinned_draw_sprite(a->scale, &a->atlas, f, r);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_preset_skins(skinned_t *s) {
|
||||||
|
s->panel = "panel";
|
||||||
|
s->button = "button";
|
||||||
|
}
|
||||||
|
|
||||||
|
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.free = skinned_free;
|
||||||
|
skinned_preset_skins(a);
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_input.c"
|
#line 1 "v4k_input.c"
|
||||||
// input framework
|
// input framework
|
||||||
// - rlyeh, public domain
|
// - rlyeh, public domain
|
||||||
|
@ -376845,22 +377210,31 @@ void editor_pump() {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
API void editor_cursorpos(int x, int y);
|
void editor_setmouse(int x, int y) {
|
||||||
void editor_cursorpos(int x, int y) {
|
|
||||||
glfwSetCursorPos( window_handle(), x, y );
|
glfwSetCursorPos( window_handle(), x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_symbol(int x, int y, const char *sym) {
|
vec2 editor_glyph(int x, int y, unsigned cp) {
|
||||||
// style: atlas size, unicode ranges and 6 font faces max
|
// 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_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}},
|
||||||
// style: 10 colors max
|
// style: 10 colors max
|
||||||
do_once font_color(FONT_COLOR1, WHITE);
|
do_once font_color(FONT_COLOR1, WHITE);
|
||||||
do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY);
|
do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY);
|
||||||
do_once font_color(FONT_COLOR3, YELLOW);
|
do_once font_color(FONT_COLOR3, YELLOW);
|
||||||
do_once font_color(FONT_COLOR4, ORANGE);
|
do_once font_color(FONT_COLOR4, ORANGE);
|
||||||
do_once font_color(FONT_COLOR5, CYAN);
|
do_once font_color(FONT_COLOR5, CYAN);
|
||||||
|
const char *sym = codepoint_to_utf8(cp);
|
||||||
font_goto(x,y);
|
font_goto(x,y);
|
||||||
font_print(va(FONT_FACE2 /*FONT_WHITE*/ FONT_H1 "%s", sym));
|
return font_print(va("%s" FONT_H1 "%s", cp >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 editor_glyphstr(int x, int y, const char *utf8) {
|
||||||
|
vec2 dim = {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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_frame( void (*game)(unsigned, float, double) ) {
|
void editor_frame( void (*game)(unsigned, float, double) ) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,886 @@
|
||||||
|
// atlasc.c
|
||||||
|
// Copyright 2019 Sepehr Taghdisian (septag@github). All rights reserved.
|
||||||
|
// License: https://github.com/septag/atlasc#license-bsd-2-clause
|
||||||
|
//
|
||||||
|
// sx_math.h
|
||||||
|
// Copyright 2018 Sepehr Taghdisian (septag@github). All rights reserved.
|
||||||
|
// License: https://github.com/septag/sx#license-bsd-2-clause
|
||||||
|
|
||||||
|
#ifndef ATLASC_HEADER
|
||||||
|
#define ATLASC_HEADER
|
||||||
|
|
||||||
|
#define ATLASC_VERSION "1.2.3"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#define ATLAS_CAST
|
||||||
|
#else
|
||||||
|
#define ATLAS_CAST(T) T
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef union vec2 { struct { float x, y; }; float f[2]; } vec2;
|
||||||
|
typedef union vec3 { struct { float x, y, z; }; float f[3]; } vec3;
|
||||||
|
typedef union vec2i { struct { int x, y; }; int n[2]; } vec2i;
|
||||||
|
typedef union recti { struct { int xmin, ymin, xmax, ymax; }; struct { vec2i vmin, vmax; }; int f[4]; } recti;
|
||||||
|
|
||||||
|
typedef struct atlas_flags {
|
||||||
|
int alpha_threshold;
|
||||||
|
float dist_threshold;
|
||||||
|
int max_width;
|
||||||
|
int max_height;
|
||||||
|
int border;
|
||||||
|
int pot;
|
||||||
|
int padding;
|
||||||
|
int mesh;
|
||||||
|
int max_verts_per_mesh;
|
||||||
|
float scale;
|
||||||
|
} atlas_flags;
|
||||||
|
|
||||||
|
typedef struct atlas_image {
|
||||||
|
uint8_t* pixels; // only supports 32bpp RGBA format
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
char *name;
|
||||||
|
} atlas_image;
|
||||||
|
|
||||||
|
typedef struct atlas_sprite {
|
||||||
|
uint8_t* src_image; // RGBA image buffer (32bpp)
|
||||||
|
vec2i src_size; // widthxheight
|
||||||
|
recti sprite_rect; // cropped rectangle relative to sprite's source image (pixels)
|
||||||
|
recti sheet_rect; // rectangle in final sheet (pixels)
|
||||||
|
char *name;
|
||||||
|
unsigned frame;
|
||||||
|
|
||||||
|
// sprite-mesh data (if flag is set. see atlas_flags)
|
||||||
|
uint16_t num_tris;
|
||||||
|
int num_points;
|
||||||
|
vec2i* pts;
|
||||||
|
vec2i* uvs;
|
||||||
|
uint16_t* tris;
|
||||||
|
} atlas_sprite;
|
||||||
|
|
||||||
|
typedef struct atlas_t {
|
||||||
|
atlas_sprite* sprites;
|
||||||
|
int num_sprites;
|
||||||
|
int* frames;
|
||||||
|
int num_frames;
|
||||||
|
atlas_image output;
|
||||||
|
} atlas_t;
|
||||||
|
|
||||||
|
// receives input files and common arguments. returns atlas_t
|
||||||
|
// you have to free the data after use with `atlas_free`
|
||||||
|
atlas_t* atlas_loadfiles(array(char*) files, atlas_flags flags);
|
||||||
|
|
||||||
|
// receives input image buffers and common arguments. returns atlas_t
|
||||||
|
// you have to free the data after use with `atlas_free`
|
||||||
|
atlas_t* atlas_loadimages(array(atlas_image) images, atlas_flags flags);
|
||||||
|
|
||||||
|
//
|
||||||
|
bool atlas_save(const char *outfile, const atlas_t* atlas, atlas_flags flags);
|
||||||
|
|
||||||
|
// frees atlas_t memory
|
||||||
|
void atlas_free(atlas_t* atlas);
|
||||||
|
|
||||||
|
// returns the last error string
|
||||||
|
const char* atlas_last_error();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // ATLASC_HEADER
|
||||||
|
|
||||||
|
//
|
||||||
|
#ifdef ATLASC_IMPLEMENTATION
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Types/Primitives
|
||||||
|
|
||||||
|
#define vec2(x,y) (ATLAS_CAST(vec2) { (float)(x), (float)(y) })
|
||||||
|
#define vec3(x,y,z) (ATLAS_CAST(vec3) { (float)(x), (float)(y), (float)(z) })
|
||||||
|
#define vec2i(x,y) (ATLAS_CAST(vec2i) { (int)(x), (int)(y) })
|
||||||
|
#define recti(x,y,X,Y) (ATLAS_CAST(recti) { (int)(x), (int)(y), (int)(X), (int)(Y) })
|
||||||
|
|
||||||
|
#define minf(a,b) ((a) < (b) ? (a) : (b))
|
||||||
|
#define maxf(a,b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define clampf(a,b,c) ( (a) < (b) ? (b) : (a) > (c) ? (c) : (a))
|
||||||
|
|
||||||
|
static int nearest_pow2(int n) { return --n, n |= n >> 1, n |= n >> 2, n |= n >> 4, n |= n >> 8, n |= n >> 16, ++n; } // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||||
|
static float sx_abs(float _a) { union { float f; unsigned int ui; } u = { _a }; return u.ui &= 0x7FFFFFFF, u.f; }
|
||||||
|
static bool equalf(float _a, float _b, float _epsilon) { const float lhs = sx_abs(_a - _b), aa = sx_abs(_a), ab = sx_abs(_b), rhs = _epsilon * maxf(1.0f, maxf(aa, ab)); return lhs <= rhs; } // http://realtimecollisiondetection.net/blog/?t=89
|
||||||
|
|
||||||
|
static vec3 cross3(const vec3 _a, const vec3 _b) { return vec3(_a.y * _b.z - _a.z * _b.y, _a.z * _b.x - _a.x * _b.z, _a.x * _b.y - _a.y * _b.x); }
|
||||||
|
|
||||||
|
static float dot2(const vec2 _a, const vec2 _b) { return _a.x * _b.x + _a.y * _b.y; }
|
||||||
|
static float len2(const vec2 _a) { return sqrt(dot2(_a, _a)); }
|
||||||
|
static vec2 norm2(const vec2 _a) { const float len = len2(_a); /*assert(len > 0 && "Divide by zero");*/ return vec2(_a.x / (len + !len), _a.y / (len + !len)); }
|
||||||
|
static vec2 add2(const vec2 _a, const vec2 _b) { return vec2(_a.x + _b.x, _a.y + _b.y); }
|
||||||
|
static vec2 sub2(const vec2 _a, const vec2 _b) { return vec2(_a.x - _b.x, _a.y - _b.y); }
|
||||||
|
static vec2 scale2(const vec2 _a, float _b) { return vec2(_a.x * _b, _a.y * _b); }
|
||||||
|
|
||||||
|
static vec2i add2i(const vec2i _a, const vec2i _b) { return vec2i(_a.x + _b.x, _a.y + _b.y); }
|
||||||
|
static vec2i sub2i(const vec2i _a, const vec2i _b) { return vec2i(_a.x - _b.x, _a.y - _b.y); }
|
||||||
|
static vec2i min2i(const vec2i _a, const vec2i _b) { return vec2i(minf(_a.x, _b.x), minf(_a.y, _b.y)); }
|
||||||
|
static vec2i max2i(const vec2i _a, const vec2i _b) { return vec2i(maxf(_a.x, _b.x), maxf(_a.y, _b.y)); }
|
||||||
|
|
||||||
|
static recti rectiwh(int _x, int _y, int _w, int _h) { return recti(_x, _y, _x + _w, _y + _h); }
|
||||||
|
static recti recti_expand(const recti rc, const vec2i expand) { return recti(rc.xmin - expand.x, rc.ymin - expand.y, rc.xmax + expand.x, rc.ymax + expand.y); }
|
||||||
|
static void recti_add_point(recti* rc, const vec2i pt) { rc->vmin = min2i(rc->vmin, pt); rc->vmax = max2i(rc->vmax, pt); }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ATLAS_REALLOC
|
||||||
|
#define ATLAS_REALLOC REALLOC
|
||||||
|
#endif
|
||||||
|
#ifndef ATLAS_MSIZE
|
||||||
|
#define ATLAS_MSIZE MSIZE
|
||||||
|
#endif
|
||||||
|
#ifndef ATLAS_CALLOC
|
||||||
|
#define ATLAS_CALLOC(n,m) memset(ATLAS_REALLOC(0, (n)*(m)), 0, (n)*(m))
|
||||||
|
#endif
|
||||||
|
#ifndef ATLAS_FREE
|
||||||
|
#define ATLAS_FREE(ptr) ((ptr) = ATLAS_REALLOC((ptr), 0))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define align_mask(_value, _mask) (((_value) + (_mask)) & ((~0) & (~(_mask))))
|
||||||
|
|
||||||
|
static void panic_if(int fail) { if(fail) exit(-fprintf(stderr, "out of memory!\n")); }
|
||||||
|
|
||||||
|
static void path_unixpath(char *buf, unsigned buflen, const char *inpath) {
|
||||||
|
snprintf(buf, buflen, "%s", inpath);
|
||||||
|
while( strchr(buf, '\\') ) *strchr(buf, '\\') = '/';
|
||||||
|
}
|
||||||
|
static void path_basename(char *buf, unsigned buflen, const char *inpath) {
|
||||||
|
const char *a = strrchr(inpath, '\\');
|
||||||
|
const char *b = strrchr(inpath, '/');
|
||||||
|
snprintf(buf, buflen, "%s", a > b ? a+1 : b > a ? b+1 : inpath );
|
||||||
|
}
|
||||||
|
static bool path_isfile(const char* filepath) {
|
||||||
|
FILE *f = fopen(filepath, "rb");
|
||||||
|
return f ? fclose(f), 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char g_error_str[512];
|
||||||
|
const char* atlas_last_error()
|
||||||
|
{
|
||||||
|
return g_error_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atlas__free_sprites(atlas_sprite* sprites, int num_sprites)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < num_sprites; i++) {
|
||||||
|
if (sprites[i].src_image) {
|
||||||
|
stbi_image_free(sprites[i].src_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sprites[i].tris) {
|
||||||
|
ATLAS_FREE(sprites[i].tris);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sprites[i].pts) {
|
||||||
|
ATLAS_FREE(sprites[i].pts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sprites[i].uvs) {
|
||||||
|
ATLAS_FREE(sprites[i].uvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sprites[i].name) {
|
||||||
|
ATLAS_FREE(sprites[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ATLAS_FREE(sprites);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atlas__blit(uint8_t* dst, int dst_x, int dst_y, int dst_pitch, const uint8_t* src,
|
||||||
|
int src_x, int src_y, int src_w, int src_h, int src_pitch, int bpp)
|
||||||
|
{
|
||||||
|
assert(dst);
|
||||||
|
assert(src);
|
||||||
|
|
||||||
|
const int pixel_sz = bpp / 8;
|
||||||
|
const uint8_t* src_ptr = src + src_y * src_pitch + src_x * pixel_sz;
|
||||||
|
uint8_t* dst_ptr = dst + dst_y * dst_pitch + dst_x * pixel_sz;
|
||||||
|
for (int y = src_y; y < (src_y + src_h); y++) {
|
||||||
|
memcpy(dst_ptr, src_ptr, src_w * pixel_sz);
|
||||||
|
src_ptr += src_pitch;
|
||||||
|
dst_ptr += dst_pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static vec2 atlas__itof2(const s2o_point p)
|
||||||
|
{
|
||||||
|
return vec2((float)p.x, (float)p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// modified version of:
|
||||||
|
// https://github.com/anael-seghezzi/Maratis-Tiny-C-library/blob/master/include/m_raster.h
|
||||||
|
static bool atlas__test_line(const uint8_t* buffer, int w, int h, s2o_point p0, s2o_point p1)
|
||||||
|
{
|
||||||
|
const uint8_t* data = buffer;
|
||||||
|
|
||||||
|
int x0 = p0.x;
|
||||||
|
int y0 = p0.y;
|
||||||
|
int x1 = p1.x;
|
||||||
|
int y1 = p1.y;
|
||||||
|
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||||
|
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||||
|
int err = dx + dy, e2;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (x0 > -1 && y0 > -1 && x0 < w && y0 < h) {
|
||||||
|
const uint8_t* pixel = data + (y0 * w + x0);
|
||||||
|
if (*pixel)
|
||||||
|
return true; // line intersects with image data
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x0 == x1 && y0 == y1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
e2 = 2 * err;
|
||||||
|
if (e2 >= dy) {
|
||||||
|
err += dy;
|
||||||
|
x0 += sx;
|
||||||
|
}
|
||||||
|
if (e2 <= dx) {
|
||||||
|
err += dx;
|
||||||
|
y0 += sy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if 'pts' buffer is changed
|
||||||
|
static bool atlas__offset_pt(s2o_point* pts, int num_pts, int pt_idx, float amount, int w, int h)
|
||||||
|
{
|
||||||
|
s2o_point ipt = pts[pt_idx];
|
||||||
|
s2o_point _ipt = ipt;
|
||||||
|
vec2 pt = atlas__itof2(ipt);
|
||||||
|
vec2 prev_pt = (pt_idx > 0) ? atlas__itof2(pts[pt_idx - 1]) : atlas__itof2(pts[num_pts - 1]);
|
||||||
|
vec2 next_pt = (pt_idx + 1) < num_pts ? atlas__itof2(pts[pt_idx + 1]) : atlas__itof2(pts[0]);
|
||||||
|
vec2 edge1 = norm2(sub2(prev_pt, pt));
|
||||||
|
vec2 edge2 = norm2(sub2(next_pt, pt));
|
||||||
|
|
||||||
|
// calculate normal vector to move the point away from the polygon
|
||||||
|
vec2 n;
|
||||||
|
vec3 c = cross3(vec3(edge1.x, edge1.y, 0), vec3(edge2.x, edge2.y, 0));
|
||||||
|
if (equalf(c.z, 0.0f, 0.00001f)) {
|
||||||
|
n = scale2(vec2(-edge1.y, edge1.x), amount);
|
||||||
|
} else {
|
||||||
|
// c.z < 0 -> point intersecting convex edges
|
||||||
|
// c.z > 0 -> point intersecting concave edges
|
||||||
|
float k = c.z < 0.0f ? -1.0f : 1.0f;
|
||||||
|
n = scale2(norm2(add2(edge1, edge2)), k * amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
pt = add2(pt, n);
|
||||||
|
ipt.x = (int)pt.x;
|
||||||
|
ipt.y = (int)pt.y;
|
||||||
|
ipt.x = clampf(ipt.x, 0, w);
|
||||||
|
ipt.y = clampf(ipt.y, 0, h);
|
||||||
|
pts[pt_idx] = ipt;
|
||||||
|
return (_ipt.x != ipt.x) || (_ipt.y != ipt.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atlas__fix_outline_pts(const uint8_t* thresholded, int tw, int th, s2o_point* pts,
|
||||||
|
int num_pts)
|
||||||
|
{
|
||||||
|
// NOTE: winding is assumed to be CW
|
||||||
|
const float offset_amount = 2.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_pts; i++) {
|
||||||
|
s2o_point pt = pts[i];
|
||||||
|
int next_i = (i + 1) < num_pts ? (i + 1) : 0;
|
||||||
|
|
||||||
|
// assert(!thresholded[pt.y * tw + pt.x]); // point shouldn't be inside threshold
|
||||||
|
|
||||||
|
s2o_point next_pt = pts[next_i];
|
||||||
|
while (atlas__test_line(thresholded, tw, th, pt, next_pt)) {
|
||||||
|
if (!atlas__offset_pt(pts, num_pts, i, offset_amount, tw, th))
|
||||||
|
break;
|
||||||
|
atlas__offset_pt(pts, num_pts, next_i, offset_amount, tw, th);
|
||||||
|
// refresh points for the new line intersection test
|
||||||
|
pt = pts[i];
|
||||||
|
next_pt = pts[next_i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void atlas__make_mesh(atlas_sprite* spr, const s2o_point* pts, int pt_count, int max_verts,
|
||||||
|
const uint8_t* thresholded, int width, int height)
|
||||||
|
{
|
||||||
|
s2o_point* temp_pts = ATLAS_CALLOC(pt_count,sizeof(s2o_point));
|
||||||
|
panic_if(!temp_pts);
|
||||||
|
|
||||||
|
memcpy(temp_pts, pts, sizeof(s2o_point)*pt_count);
|
||||||
|
int num_verts = pt_count;
|
||||||
|
|
||||||
|
if (width > 1 && height > 1) {
|
||||||
|
const float delta = 0.5f;
|
||||||
|
const float threshold_start = 0.5f;
|
||||||
|
float threshold = threshold_start;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
s2o_distance_based_path_simplification(temp_pts, &num_verts, threshold);
|
||||||
|
|
||||||
|
if(num_verts <= max_verts) break;
|
||||||
|
|
||||||
|
memcpy(temp_pts, pts, sizeof(s2o_point)*pt_count);
|
||||||
|
num_verts = pt_count;
|
||||||
|
|
||||||
|
threshold += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix any collisions with the actual image // @r-lyeh: method below is buggy. will return dupe points
|
||||||
|
atlas__fix_outline_pts(thresholded, width, height, temp_pts, num_verts);
|
||||||
|
}
|
||||||
|
|
||||||
|
//< @r-lyeh: remove dupes
|
||||||
|
for (int i = 0; i < num_verts - 1; i++) {
|
||||||
|
for (int j = i + 1; j < num_verts; j++) {
|
||||||
|
if( temp_pts[i].x == temp_pts[j].x && temp_pts[i].y == temp_pts[j].y ) {
|
||||||
|
temp_pts[j].x = temp_pts[num_verts - 1].x;
|
||||||
|
temp_pts[j].y = temp_pts[num_verts - 1].y;
|
||||||
|
--num_verts;
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//<
|
||||||
|
|
||||||
|
// triangulate
|
||||||
|
del_point2d_t* dpts = ATLAS_CALLOC(num_verts, sizeof(del_point2d_t));
|
||||||
|
panic_if(!dpts);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_verts; i++) {
|
||||||
|
dpts[i].x = (double)temp_pts[i].x;
|
||||||
|
dpts[i].y = (double)temp_pts[i].y;
|
||||||
|
//printf("%d) %f,%f\n", i, dpts[i].x, dpts[i].y); //< @r-lyeh: debug dupe points
|
||||||
|
}
|
||||||
|
|
||||||
|
delaunay2d_t* polys = delaunay2d_from(dpts, num_verts);
|
||||||
|
assert(polys);
|
||||||
|
tri_delaunay2d_t* tris = tri_delaunay2d_from(polys);
|
||||||
|
assert(tris);
|
||||||
|
ATLAS_FREE(dpts);
|
||||||
|
delaunay2d_release(polys);
|
||||||
|
|
||||||
|
assert(tris->num_triangles < UINT16_MAX);
|
||||||
|
spr->tris = ATLAS_CALLOC(tris->num_triangles * 3,sizeof(uint16_t));
|
||||||
|
spr->pts = ATLAS_CALLOC(tris->num_points, sizeof(vec2i));
|
||||||
|
assert(spr->tris);
|
||||||
|
assert(spr->pts);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < tris->num_triangles; i++) {
|
||||||
|
unsigned int index = i * 3;
|
||||||
|
spr->tris[index] = (uint16_t)tris->tris[index];
|
||||||
|
spr->tris[index + 1] = (uint16_t)tris->tris[index + 1];
|
||||||
|
spr->tris[index + 2] = (uint16_t)tris->tris[index + 2];
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < tris->num_points; i++) {
|
||||||
|
spr->pts[i] = vec2i((int)tris->points[i].x, (int)tris->points[i].y);
|
||||||
|
}
|
||||||
|
spr->num_tris = (uint16_t)tris->num_triangles;
|
||||||
|
spr->num_points = (int)tris->num_points;
|
||||||
|
|
||||||
|
tri_delaunay2d_release(tris);
|
||||||
|
ATLAS_FREE(temp_pts);
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas_t* atlas_loadimages(array(atlas_image) images, atlas_flags flags)
|
||||||
|
{
|
||||||
|
assert(images);
|
||||||
|
|
||||||
|
array(int) frames = 0;
|
||||||
|
array(atlas_sprite) sprites = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < array_count(images); i++) {
|
||||||
|
|
||||||
|
// find is_cached
|
||||||
|
{
|
||||||
|
int found = 0, k = 0;
|
||||||
|
static array(uint64_t) cache = 0;
|
||||||
|
static array(uint64_t) index = 0;
|
||||||
|
uint64_t hash = hash_init;
|
||||||
|
hash = hash_bin(&images[i].width, sizeof(images[i].width), hash);
|
||||||
|
hash = hash_bin(&images[i].height, sizeof(images[i].height), hash);
|
||||||
|
hash = hash_bin((char*)images[i].pixels, images[i].width * images[i].height * 4, hash);
|
||||||
|
for (; k < array_count(cache); ++k)
|
||||||
|
if (cache[k] == hash) { found = 1; break; }
|
||||||
|
if (found) {
|
||||||
|
array_push(frames, index[k]);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
array_push(cache, hash);
|
||||||
|
array_push(index, k);
|
||||||
|
array_push(frames, k);
|
||||||
|
}
|
||||||
|
//printf("%d) %llx\n", array_count(cache), hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas_sprite zero = {0};
|
||||||
|
atlas_sprite* spr = &zero;
|
||||||
|
if(images[i].name) spr->name = STRDUP(images[i].name);
|
||||||
|
spr->frame = i;
|
||||||
|
|
||||||
|
spr->src_size.x = images[i].width;
|
||||||
|
spr->src_size.y = images[i].height;
|
||||||
|
assert(images[i].width > 0 && images[i].height > 0);
|
||||||
|
assert(images[i].pixels);
|
||||||
|
uint8_t* pixels = images[i].pixels;
|
||||||
|
|
||||||
|
// rescale
|
||||||
|
if (!equalf(flags.scale, 1.0f, 0.0001f)) {
|
||||||
|
int target_w = (int)((float)spr->src_size.x * flags.scale);
|
||||||
|
int target_h = (int)((float)spr->src_size.y * flags.scale);
|
||||||
|
uint8_t* resized_pixels = ATLAS_CALLOC(1, 4 * target_w * target_h);
|
||||||
|
panic_if(!resized_pixels);
|
||||||
|
|
||||||
|
if (!stbir_resize_uint8(pixels, spr->src_size.x, spr->src_size.y, 4 * spr->src_size.x,
|
||||||
|
resized_pixels, target_w, target_h, 4 * target_w, 4)) {
|
||||||
|
snprintf(g_error_str, sizeof(g_error_str), "could not resize image: #%d", i + 1);
|
||||||
|
atlas__free_sprites(sprites, array_count(sprites));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_image_free(pixels);
|
||||||
|
|
||||||
|
spr->src_size.x = target_w;
|
||||||
|
spr->src_size.y = target_h;
|
||||||
|
pixels = resized_pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
spr->src_image = pixels;
|
||||||
|
|
||||||
|
recti sprite_rect = {0};
|
||||||
|
int pt_count = 0;
|
||||||
|
s2o_point* pts = 0;
|
||||||
|
uint8_t* alpha = s2o_rgba_to_alpha(spr->src_image, spr->src_size.x, spr->src_size.y);
|
||||||
|
uint8_t* thresholded = s2o_alpha_to_thresholded(alpha, spr->src_size.x, spr->src_size.y, flags.alpha_threshold);
|
||||||
|
free(alpha);
|
||||||
|
|
||||||
|
if (flags.mesh && spr->src_size.x > 1 && spr->src_size.y > 1) {
|
||||||
|
uint8_t* dilate_thres = s2o_dilate_thresholded(thresholded, spr->src_size.x, spr->src_size.y);
|
||||||
|
|
||||||
|
uint8_t* outlined = s2o_thresholded_to_outlined(dilate_thres, spr->src_size.x, spr->src_size.y);
|
||||||
|
free(dilate_thres);
|
||||||
|
|
||||||
|
pts = s2o_extract_outline_path(outlined, spr->src_size.x, spr->src_size.y, &pt_count, NULL);
|
||||||
|
free(outlined);
|
||||||
|
|
||||||
|
//< @r-lyeh @fixme: many sprites will return extremely low num of points (like 8) even if the sprite is complex enough.
|
||||||
|
//< this will lead to produce here a nearly zero sprite_rect, then sheet_rect, then eventually an empty frame at end of pipeline.
|
||||||
|
|
||||||
|
// calculate cropped rectangle
|
||||||
|
sprite_rect = recti(INT_MAX, INT_MAX, INT_MIN, INT_MIN);
|
||||||
|
for (int k = 0; k < pt_count; k++) {
|
||||||
|
recti_add_point(&sprite_rect, vec2i(pts[k].x, pts[k].y));
|
||||||
|
}
|
||||||
|
sprite_rect.xmax++;
|
||||||
|
sprite_rect.ymax++;
|
||||||
|
} else {
|
||||||
|
sprite_rect = recti(0, 0, spr->src_size.x, spr->src_size.y);
|
||||||
|
pt_count = 4;
|
||||||
|
pts = ATLAS_CALLOC(pt_count, sizeof(s2o_point));
|
||||||
|
pts[0] = (s2o_point) {0, 0};
|
||||||
|
pts[1] = (s2o_point) {spr->src_size.x, 0};
|
||||||
|
pts[2] = (s2o_point) {spr->src_size.x, spr->src_size.y};
|
||||||
|
pts[3] = (s2o_point) {0, spr->src_size.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate mesh if set in arguments
|
||||||
|
if (flags.mesh) {
|
||||||
|
atlas__make_mesh(spr, pts, pt_count, flags.max_verts_per_mesh, thresholded,
|
||||||
|
spr->src_size.x, spr->src_size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
ATLAS_FREE(pts);
|
||||||
|
free(thresholded);
|
||||||
|
spr->sprite_rect = sprite_rect;
|
||||||
|
|
||||||
|
array_push(sprites, *spr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_sprites = array_count(sprites);
|
||||||
|
|
||||||
|
// pack sprites into a sheet
|
||||||
|
stbrp_context rp_ctx = {0};
|
||||||
|
int max_width = flags.max_width;
|
||||||
|
int max_height = flags.max_height;
|
||||||
|
int num_rp_nodes = max_width + max_height;
|
||||||
|
stbrp_rect* rp_rects = ATLAS_CALLOC(num_sprites, sizeof(stbrp_rect));
|
||||||
|
stbrp_node* rp_nodes = ATLAS_CALLOC(num_rp_nodes, sizeof(stbrp_node));
|
||||||
|
panic_if(!rp_rects || !rp_nodes);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_sprites; i++) {
|
||||||
|
recti rc = sprites[i].sprite_rect;
|
||||||
|
int rc_resize = (flags.border + flags.padding) * 2;
|
||||||
|
rp_rects[i].w = (rc.xmax - rc.xmin) + rc_resize;
|
||||||
|
rp_rects[i].h = (rc.ymax - rc.ymin) + rc_resize;
|
||||||
|
}
|
||||||
|
stbrp_init_target(&rp_ctx, max_width, max_height, rp_nodes, num_rp_nodes);
|
||||||
|
recti final_rect = recti(INT_MAX, INT_MAX, INT_MIN, INT_MIN);
|
||||||
|
if (stbrp_pack_rects(&rp_ctx, rp_rects, num_sprites)) {
|
||||||
|
for (int i = 0; i < num_sprites; i++) {
|
||||||
|
atlas_sprite* spr = &sprites[i];
|
||||||
|
recti sheet_rect = rectiwh(rp_rects[i].x, rp_rects[i].y, rp_rects[i].w, rp_rects[i].h);
|
||||||
|
|
||||||
|
// calculate the total size of output image
|
||||||
|
recti_add_point(&final_rect, sheet_rect.vmin);
|
||||||
|
recti_add_point(&final_rect, sheet_rect.vmax);
|
||||||
|
|
||||||
|
// shrink back rect and set the real sheet_rect for the sprite
|
||||||
|
spr->sheet_rect =
|
||||||
|
recti_expand(sheet_rect, vec2i(-flags.border, -flags.border));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int dst_w = final_rect.xmax - final_rect.xmin;
|
||||||
|
int dst_h = final_rect.ymax - final_rect.ymin;
|
||||||
|
// make output size divide by 4 by default
|
||||||
|
dst_w = align_mask(dst_w, 3);
|
||||||
|
dst_h = align_mask(dst_h, 3);
|
||||||
|
|
||||||
|
if (flags.pot) {
|
||||||
|
dst_w = nearest_pow2(dst_w);
|
||||||
|
dst_h = nearest_pow2(dst_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* dst = ATLAS_CALLOC(1, dst_w * dst_h * 4);
|
||||||
|
panic_if(!dst);
|
||||||
|
|
||||||
|
// calculate UVs for sprite meshes
|
||||||
|
if (flags.mesh) {
|
||||||
|
for (int i = 0; i < num_sprites; i++) {
|
||||||
|
atlas_sprite* spr = &sprites[i];
|
||||||
|
// if sprite has mesh, calculate UVs for it
|
||||||
|
if (spr->pts && spr->num_points) {
|
||||||
|
const int padding = flags.padding;
|
||||||
|
vec2i offset = spr->sprite_rect.vmin;
|
||||||
|
vec2i sheet_pos =
|
||||||
|
vec2i(spr->sheet_rect.xmin + padding, spr->sheet_rect.ymin + padding);
|
||||||
|
vec2i* uvs = ATLAS_CALLOC(spr->num_points, sizeof(vec2i));
|
||||||
|
assert(uvs);
|
||||||
|
for (int pi = 0; pi < spr->num_points; pi++) {
|
||||||
|
vec2i pt = spr->pts[pi];
|
||||||
|
uvs[pi] = add2i(sub2i(pt, offset), sheet_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
spr->uvs = uvs;
|
||||||
|
} // generate uvs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_sprites; i++) {
|
||||||
|
const atlas_sprite* spr = &sprites[i];
|
||||||
|
|
||||||
|
// calculate UVs for sprite-meshes
|
||||||
|
|
||||||
|
// remove padding and blit from src_image to dst
|
||||||
|
recti dstrc = recti_expand(spr->sheet_rect, vec2i(-flags.padding, -flags.padding));
|
||||||
|
recti srcrc = spr->sprite_rect;
|
||||||
|
atlas__blit(dst, dstrc.xmin, dstrc.ymin, dst_w * 4, spr->src_image, srcrc.xmin, srcrc.ymin,
|
||||||
|
srcrc.xmax - srcrc.xmin, srcrc.ymax - srcrc.ymin, spr->src_size.x * 4, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas_t* atlas = ATLAS_CALLOC(1, sizeof(atlas_t));
|
||||||
|
panic_if(!atlas);
|
||||||
|
|
||||||
|
atlas->output.pixels = dst;
|
||||||
|
atlas->output.width = dst_w;
|
||||||
|
atlas->output.height = dst_h;
|
||||||
|
atlas->sprites = sprites;
|
||||||
|
atlas->num_sprites = num_sprites;
|
||||||
|
atlas->frames = frames;
|
||||||
|
atlas->num_frames = array_count(frames);
|
||||||
|
|
||||||
|
ATLAS_FREE(rp_nodes);
|
||||||
|
ATLAS_FREE(rp_rects);
|
||||||
|
|
||||||
|
return atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *atlas_anims = 0;
|
||||||
|
static char *atlas_slices = 0;
|
||||||
|
static char *atlas_current_anim = 0;
|
||||||
|
|
||||||
|
atlas_t* atlas_loadfiles(array(char*) files, atlas_flags flags)
|
||||||
|
{
|
||||||
|
assert(files);
|
||||||
|
|
||||||
|
array(atlas_image) images = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < array_count(files); ++i) {
|
||||||
|
if (!path_isfile(files[i])) {
|
||||||
|
snprintf(g_error_str, sizeof(g_error_str), "input image not found: %s", files[i]);
|
||||||
|
goto err_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
int comp;
|
||||||
|
atlas_image img = {0};
|
||||||
|
img.pixels = stbi_load(files[i], &img.width, &img.height, &comp, 4);
|
||||||
|
|
||||||
|
#ifdef CUTE_ASEPRITE_H
|
||||||
|
if (!img.pixels) {
|
||||||
|
bool loaded = 0;
|
||||||
|
|
||||||
|
for( ase_t* ase = cute_aseprite_load_from_file(files[i], NULL); ase; cute_aseprite_free(ase), ase = 0, loaded = 1) {
|
||||||
|
ase_tag_t *parent = ase->tags + 0;
|
||||||
|
|
||||||
|
//< abc/def/ghi.aseprite -> ghi
|
||||||
|
if( atlas_current_anim ) *atlas_current_anim = '\0';
|
||||||
|
strcatf(&atlas_current_anim, files[i]);
|
||||||
|
path_basename(atlas_current_anim, strlen(atlas_current_anim), files[i]);
|
||||||
|
if( strrchr(atlas_current_anim, '.')) *strrchr(atlas_current_anim, '.') = '\0';
|
||||||
|
trimspace(atlas_current_anim);
|
||||||
|
//<
|
||||||
|
|
||||||
|
for( int f = 0; f < ase->frame_count; ++f) {
|
||||||
|
ase_frame_t *frame = ase->frames + f;
|
||||||
|
|
||||||
|
// find rect
|
||||||
|
int x = INT_MAX, y = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
|
||||||
|
for( int c = 0; c < frame->cel_count; ++c ) {
|
||||||
|
ase_cel_t *cel = frame->cels + c;
|
||||||
|
if( cel->layer->flags & ASE_LAYER_FLAGS_VISIBLE ) {
|
||||||
|
if( cel->x < x ) x = cel->x;
|
||||||
|
if( cel->h < y ) y = cel->y;
|
||||||
|
if( (cel->x + cel->w) > x2 ) x2 = cel->x + cel->w;
|
||||||
|
if( (cel->y + cel->h) > y2 ) y2 = cel->y + cel->h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x2 <= 0 || y2 <= 0) { // submit empty frame
|
||||||
|
img.width = 1;
|
||||||
|
img.height = 1;
|
||||||
|
img.pixels = calloc(1, 1*1*4);
|
||||||
|
array_push(images, img);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int cx = x;
|
||||||
|
int cy = y;
|
||||||
|
int cw = x2-x;
|
||||||
|
int ch = y2-y;
|
||||||
|
int tn = 4;
|
||||||
|
int tw = ase->w;
|
||||||
|
|
||||||
|
// find clip
|
||||||
|
img.width = cw;
|
||||||
|
img.height = ch;
|
||||||
|
img.pixels = calloc(1, cw*ch*4); // @fixme: because of a stbi_image_free() within rescale section, this should be allocated with stbi allocator
|
||||||
|
for( unsigned y = 0; y < ch; ++y )
|
||||||
|
memcpy((char *)img.pixels + (0+(0+y)*cw)*tn, (char*)frame->pixels + (cx+(cy+y)*tw)*tn, cw*tn);
|
||||||
|
array_push(images, img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int slice_idx = -1;
|
||||||
|
static int slice_frame_idx = 0;
|
||||||
|
static const char *slice_name = 0;
|
||||||
|
if(!atlas_slices) strcatf(&atlas_slices, "[slices]\n");
|
||||||
|
|
||||||
|
for( int t = 0; t < ase->slice_count; ++t) {
|
||||||
|
ase_slice_t *slice = ase->slices + t;
|
||||||
|
if (!slice_name || strcmp(slice_name, slice->name)) {
|
||||||
|
++slice_idx;
|
||||||
|
strcatf(&atlas_slices, "[%d].sl_name=%s\n", slice_idx, slice->name);
|
||||||
|
strcatf(&atlas_slices, "[%d].sl_frames=", slice_idx);
|
||||||
|
for( int u = 0; u < ase->slice_count; ++u) {
|
||||||
|
if (!strcmp(slice->name, ase->slices[u].name)) {
|
||||||
|
strcatf(&atlas_slices, "%d,", u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strcatf(&atlas_slices, "\n");
|
||||||
|
}
|
||||||
|
strcatf(&atlas_slices, "[%d].sl_bounds=%d,%d,%d,%d\n", slice_idx, slice->origin_x, slice->origin_y, slice->w, slice->h);
|
||||||
|
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);
|
||||||
|
|
||||||
|
slice_name = slice->name;
|
||||||
|
++slice_frame_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int anim_idx = 0;
|
||||||
|
if(!atlas_anims) strcatf(&atlas_anims, "[anims]\n");
|
||||||
|
|
||||||
|
for( int t = 0; t < ase->tag_count; ++t) {
|
||||||
|
ase_tag_t *tag = ase->tags + t;
|
||||||
|
|
||||||
|
// find full name
|
||||||
|
int range[2] = {tag->from_frame, tag->to_frame};
|
||||||
|
char name[256] = {0};
|
||||||
|
for( int tt = 0; tt < ase->tag_count; ++tt ) {
|
||||||
|
ase_tag_t *ttag = ase->tags + tt;
|
||||||
|
if( range[0] >= ttag->from_frame && range[1] <= ttag->to_frame )
|
||||||
|
strcat(name, "."), strcat(name, ttag->name);
|
||||||
|
}
|
||||||
|
trimspace(name);
|
||||||
|
|
||||||
|
char *sep = "";
|
||||||
|
strcatf(&atlas_anims, "[%d].name=%s.%s\n", anim_idx, atlas_current_anim, name+1);
|
||||||
|
strcatf(&atlas_anims, "[%d].frames=", anim_idx);
|
||||||
|
if( tag->loop_animation_direction != ASE_ANIMATION_DIRECTION_BACKWARDS)
|
||||||
|
for( int from = tag->from_frame; from <= tag->to_frame; ++from ) {
|
||||||
|
strcatf(&atlas_anims, "%s%d,%d", sep, from, ase->frames[from].duration_milliseconds), sep = ",";
|
||||||
|
}
|
||||||
|
sep = "";
|
||||||
|
if( tag->loop_animation_direction != ASE_ANIMATION_DIRECTION_FORWARDS)
|
||||||
|
for( int from = tag->from_frame; from <= tag->to_frame; ++from ) {
|
||||||
|
strcatf(&atlas_anims, "%s%d,%d", sep, from, ase->frames[from].duration_milliseconds), sep = ",";
|
||||||
|
}
|
||||||
|
strcatf(&atlas_anims,"\n");
|
||||||
|
|
||||||
|
++anim_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( loaded ) continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!img.pixels) {
|
||||||
|
continue; //< @r-lyeh: keep going
|
||||||
|
|
||||||
|
snprintf(g_error_str, sizeof(g_error_str), "invalid image format: %s", files[i]);
|
||||||
|
goto err_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !img.name ) img.name = STRDUP(files[i]);
|
||||||
|
|
||||||
|
array_push(images, img);
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas_t* atlas = atlas_loadimages(images, flags);
|
||||||
|
return atlas;
|
||||||
|
|
||||||
|
err_cleanup:
|
||||||
|
for (int i = 0; i < array_count(images); i++) {
|
||||||
|
if (images[i].pixels) {
|
||||||
|
stbi_image_free(images[i].pixels);
|
||||||
|
}
|
||||||
|
if (images[i].name) {
|
||||||
|
ATLAS_FREE(images[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
array_free(images);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void atlas_free(atlas_t* atlas)
|
||||||
|
{
|
||||||
|
assert(atlas);
|
||||||
|
|
||||||
|
if (atlas->sprites)
|
||||||
|
atlas__free_sprites(atlas->sprites, atlas->num_sprites);
|
||||||
|
if (atlas->frames)
|
||||||
|
ATLAS_FREE(atlas->frames);
|
||||||
|
if (atlas->output.pixels)
|
||||||
|
ATLAS_FREE(atlas->output.pixels);
|
||||||
|
ATLAS_FREE(atlas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// custom write function
|
||||||
|
typedef struct {
|
||||||
|
int offset;
|
||||||
|
void *buffer;
|
||||||
|
} stbi_mem_context;
|
||||||
|
static void stbi_write_mem(void *context, void *data, int size) {
|
||||||
|
stbi_mem_context *ctx = (stbi_mem_context*)context;
|
||||||
|
memcpy( ctx->buffer, data, size );
|
||||||
|
ctx->offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool atlas_save(const char *outfile, const atlas_t *atlas, atlas_flags flags)
|
||||||
|
{
|
||||||
|
assert(outfile);
|
||||||
|
|
||||||
|
const bool is_file = strcmp(outfile, "stdout");
|
||||||
|
const atlas_sprite* sprites = atlas->sprites;
|
||||||
|
const int* frames = atlas->frames;
|
||||||
|
const int num_frames = atlas->num_frames;
|
||||||
|
const int num_sprites = atlas->num_sprites;
|
||||||
|
const uint8_t* dst = atlas->output.pixels;
|
||||||
|
const int dst_w = atlas->output.width;
|
||||||
|
const int dst_h = atlas->output.height;
|
||||||
|
|
||||||
|
char image_filepath[256];
|
||||||
|
char image_filename[256];
|
||||||
|
snprintf(image_filepath, sizeof(image_filepath), "%s.png", outfile);
|
||||||
|
path_basename(image_filename, sizeof(image_filename), image_filepath);
|
||||||
|
|
||||||
|
stbi_write_png_compression_level = 5; // 8
|
||||||
|
|
||||||
|
// write texture, if needed
|
||||||
|
if( is_file ) {
|
||||||
|
if (!stbi_write_png(image_filepath, dst_w, dst_h, 4, dst, dst_w * 4)) {
|
||||||
|
fprintf(stderr, "could not write image file `%s`\n", image_filepath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write atlas description into .ini file
|
||||||
|
FILE *writer = is_file ? fopen(outfile, "wt") : stdout;
|
||||||
|
if (!writer) {
|
||||||
|
fprintf(stderr, "could not write ini file `%s`\n", outfile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(writer, "[atlas]\n");
|
||||||
|
|
||||||
|
if (is_file) {
|
||||||
|
fprintf(writer, "file=%s\n", image_filepath);
|
||||||
|
} else {
|
||||||
|
stbi_mem_context ctx = {0, ATLAS_CALLOC(1, dst_w*dst_h*4+256) };
|
||||||
|
int result = stbi_write_png_to_func(stbi_write_mem, &ctx, dst_w, dst_h, 4, dst, dst_w*4);
|
||||||
|
char *b64 = base64_encode(ctx.buffer, ctx.offset);
|
||||||
|
fprintf(writer, "bitmap=%s\n", b64); // %d:%s\n", ctx.offset, b64);
|
||||||
|
ATLAS_FREE(ctx.buffer);
|
||||||
|
FREE(b64);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(writer, "size=%d,%d\n", dst_w, dst_h);
|
||||||
|
fprintf(writer, "border=%d,%d\n", flags.border, flags.border);
|
||||||
|
fprintf(writer, "padding=%d,%d\n", flags.padding, flags.padding);
|
||||||
|
|
||||||
|
for( int i = 0; i < num_frames; i++ ) {
|
||||||
|
const atlas_sprite* spr = sprites + frames[i];
|
||||||
|
|
||||||
|
char name[256];
|
||||||
|
path_unixpath(name, sizeof(name), spr->name ? spr->name : "");
|
||||||
|
|
||||||
|
if(name[0])
|
||||||
|
fprintf(writer, "[%d].name=%s\n", i, name);
|
||||||
|
fprintf(writer, "[%d].frame=%u\n", i, spr->frame);
|
||||||
|
//fprintf(writer, "[%d].size=%d,%d\n", i, spr->src_size.n[0], spr->src_size.n[1]);
|
||||||
|
//fprintf(writer, "[%d].rect=%u,%u,%u,%u\n", i, spr->sprite_rect.f[0], spr->sprite_rect.f[1], spr->sprite_rect.f[2], spr->sprite_rect.f[3]);
|
||||||
|
fprintf(writer, "[%d].sheet=%u,%u,%u,%u\n", i, spr->sheet_rect.f[0], spr->sheet_rect.f[1], spr->sheet_rect.f[2], spr->sheet_rect.f[3]);
|
||||||
|
if( spr->num_tris ) {
|
||||||
|
fprintf(writer, "[%d].indices=", i); // %d:", i, (int)spr->num_tris * 3);
|
||||||
|
for( int j = 0, jend = (int)spr->num_tris * 3; j < jend; ++j )
|
||||||
|
fprintf(writer, "%u%s", spr->tris[j], j < (jend-1) ? "," : "\n");
|
||||||
|
|
||||||
|
fprintf(writer, "[%d].coords=", i); // %d:", i, spr->num_points*2);
|
||||||
|
for( int j = 0, jend = spr->num_points; j < jend; j++ )
|
||||||
|
fprintf(writer, "%.f,%.f%s", (double)spr->pts[j].x, (double)spr->pts[j].y, j < (jend-1) ? ",":"\n" );
|
||||||
|
|
||||||
|
fprintf(writer, "[%d].uvs=", i); // %d:", i, spr->num_points*2);
|
||||||
|
for( int j = 0, jend = spr->num_points; j < jend; j++ )
|
||||||
|
fprintf(writer, "%.f,%.f%s", (double)spr->uvs[j].x, (double)spr->uvs[j].y, j < (jend-1) ? ",":"\n" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( atlas_anims ) fprintf(writer, "%s\n", atlas_anims);
|
||||||
|
if( atlas_slices ) fprintf(writer, "%s\n", atlas_slices);
|
||||||
|
|
||||||
|
if(writer != stdout) fclose(writer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ATLASC_IMPLEMENTATION
|
File diff suppressed because it is too large
Load Diff
|
@ -4,11 +4,11 @@
|
||||||
#ifndef ICON_MD_H
|
#ifndef ICON_MD_H
|
||||||
#define ICON_MD_H
|
#define ICON_MD_H
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MD "MaterialIcons-Regular.ttf"
|
#define ICON_MD_FILENAME "MaterialIcons-Regular.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MD 0xe000
|
#define ICON_MD_MIN 0xe000
|
||||||
#define ICON_MAX_16_MD 0xf8ff
|
#define ICON_MD_MAX_16 0xf8ff
|
||||||
#define ICON_MAX_MD 0x10fffd
|
#define ICON_MD_MAX 0x10fffd
|
||||||
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
||||||
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
||||||
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
// for use with https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf
|
// for use with https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MDI "materialdesignicons-webfont.ttf"
|
#define ICON_MDI_FILENAME "materialdesignicons-webfont.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MDI 0xF68C
|
#define ICON_MDI_MIN 0xF68C
|
||||||
#define ICON_MAX_16_MDI 0xF68C
|
#define ICON_MDI_MAX_16 0xF68C
|
||||||
#define ICON_MAX_MDI 0xF1CC7
|
#define ICON_MDI_MAX 0xF1CC7
|
||||||
#define ICON_MDI_AB_TESTING "\xf3\xb0\x87\x89" // U+F01C9
|
#define ICON_MDI_AB_TESTING "\xf3\xb0\x87\x89" // U+F01C9
|
||||||
#define ICON_MDI_ABACUS "\xf3\xb1\x9b\xa0" // U+F16E0
|
#define ICON_MDI_ABACUS "\xf3\xb1\x9b\xa0" // U+F16E0
|
||||||
#define ICON_MDI_ABJAD_ARABIC "\xf3\xb1\x8c\xa8" // U+F1328
|
#define ICON_MDI_ABJAD_ARABIC "\xf3\xb1\x8c\xa8" // U+F1328
|
||||||
|
|
|
@ -125,7 +125,7 @@ struct RenFont {
|
||||||
|
|
||||||
static struct { int left, top, right, bottom; } lt_clip;
|
static struct { int left, top, right, bottom; } lt_clip;
|
||||||
|
|
||||||
static const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
static const char* codepoint_to_utf8_(unsigned c) { //< @r-lyeh
|
||||||
static char s[4+1];
|
static char s[4+1];
|
||||||
lt_memset(s, 0, 5);
|
lt_memset(s, 0, 5);
|
||||||
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
||||||
|
@ -134,7 +134,7 @@ static const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
||||||
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
static const char* utf8_to_codepoint_(const char *p, unsigned *dst) {
|
||||||
unsigned res, n;
|
unsigned res, n;
|
||||||
switch (*p & 0xf0) {
|
switch (*p & 0xf0) {
|
||||||
case 0xf0 : res = *p & 0x07; n = 3; break;
|
case 0xf0 : res = *p & 0x07; n = 3; break;
|
||||||
|
@ -310,7 +310,7 @@ int ren_get_font_width(RenFont *font, const char *text) {
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint_(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSet *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
||||||
x += g->xadvance;
|
x += g->xadvance;
|
||||||
|
@ -415,7 +415,7 @@ int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color)
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint_(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSet *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
||||||
rect.x = g->x0;
|
rect.x = g->x0;
|
||||||
|
|
|
@ -206,7 +206,7 @@ int printi(int i) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* codepoint_to_utf8(unsigned c);
|
static const char* codepoint_to_utf8_(unsigned c);
|
||||||
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char buf[16];
|
char buf[16];
|
||||||
|
@ -250,7 +250,7 @@ int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved
|
||||||
goto bottom;
|
goto bottom;
|
||||||
|
|
||||||
break; case GLEQ_CODEPOINT_INPUT:
|
break; case GLEQ_CODEPOINT_INPUT:
|
||||||
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8(e.codepoint));
|
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
|
||||||
|
|
||||||
break; case GLEQ_BUTTON_PRESSED:
|
break; case GLEQ_BUTTON_PRESSED:
|
||||||
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
||||||
|
|
|
@ -0,0 +1,464 @@
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Licensing information can be found at the end of the file.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
mid.h - v0.1 - Midi playback library using the TinySoundFont library.
|
||||||
|
|
||||||
|
Do this:
|
||||||
|
#define MID_IMPLEMENTATION
|
||||||
|
before you include this file in *one* C/C++ file to create the implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef mid_h
|
||||||
|
#define mid_h
|
||||||
|
|
||||||
|
#define _CRT_NONSTDC_NO_DEPRECATE
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct mid_t mid_t;
|
||||||
|
typedef struct tsf tsf;
|
||||||
|
|
||||||
|
mid_t* mid_create( void const* midi_data, size_t midi_size, void* memctx );
|
||||||
|
void mid_destroy( mid_t* mid );
|
||||||
|
|
||||||
|
int mid_render_short( mid_t* mid, short* sample_pairs, int sample_pairs_count, tsf* sound_font );
|
||||||
|
int mid_render_float( mid_t* mid, float* sample_pairs, int sample_pairs_count, tsf* sound_font );
|
||||||
|
|
||||||
|
void mid_skip_leading_silence( mid_t* mid, tsf* sound_font );
|
||||||
|
|
||||||
|
#endif /* mid_h */
|
||||||
|
|
||||||
|
#ifdef MID_ENABLE_RAW
|
||||||
|
|
||||||
|
#ifndef mid_raw_h
|
||||||
|
#define mid_raw_h
|
||||||
|
|
||||||
|
#ifndef MID_U8
|
||||||
|
#define MID_U8 unsigned char
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_U16
|
||||||
|
#define MID_U16 unsigned short
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_U32
|
||||||
|
#define MID_U32 unsigned int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_U64
|
||||||
|
#define MID_U64 unsigned long long
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct mid_event_t
|
||||||
|
{
|
||||||
|
MID_U32 delay_us;
|
||||||
|
MID_U8 channel;
|
||||||
|
MID_U8 type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct { MID_U8 program; } program_change;
|
||||||
|
struct { MID_U8 note; MID_U8 velocity; } note_on;
|
||||||
|
struct { MID_U8 note; } note_off;
|
||||||
|
struct { MID_U8 key; MID_U8 key_pressure; } key_pressure;
|
||||||
|
struct { MID_U16 value; } pitch_bend;
|
||||||
|
struct { MID_U8 control, control_value; } control_change;
|
||||||
|
struct { MID_U8 channel_pressure; } channel_pressure;
|
||||||
|
} data;
|
||||||
|
} mid_event_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct mid_song_t
|
||||||
|
{
|
||||||
|
int event_count;
|
||||||
|
mid_event_t* events;
|
||||||
|
} mid_song_t;
|
||||||
|
|
||||||
|
|
||||||
|
struct mid_t
|
||||||
|
{
|
||||||
|
void* memctx;
|
||||||
|
mid_song_t song;
|
||||||
|
int percussion_preset;
|
||||||
|
MID_U64 playback_accumulated_time_us;
|
||||||
|
int playback_sample_pos;
|
||||||
|
int playback_event_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
int mid_init_raw( mid_t* mid, void const* raw_data, size_t raw_size );
|
||||||
|
|
||||||
|
size_t mid_save_raw( mid_t* mid, void* data, size_t capacity );
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MID_ENABLE_RAW */
|
||||||
|
|
||||||
|
#endif /* mid_raw_h */
|
||||||
|
|
||||||
|
/*
|
||||||
|
----------------------
|
||||||
|
IMPLEMENTATION
|
||||||
|
----------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MID_IMPLEMENTATION
|
||||||
|
#undef MID_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifndef MID_U8
|
||||||
|
#define MID_U8 unsigned char
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_U16
|
||||||
|
#define MID_U16 unsigned short
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_U32
|
||||||
|
#define MID_U32 unsigned int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_U64
|
||||||
|
#define MID_U64 unsigned long long
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MID_MALLOC
|
||||||
|
#define _CRT_NONSTDC_NO_DEPRECATE
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stdlib.h>
|
||||||
|
#if defined(_cplusplus)
|
||||||
|
#define MID_MALLOC( ctx, size ) ( ::malloc( size ) )
|
||||||
|
#define MID_FREE( ctx, ptr ) ( ::free( ptr ) )
|
||||||
|
#else
|
||||||
|
#define MID_MALLOC( ctx, size ) ( malloc( size ) )
|
||||||
|
#define MID_FREE( ctx, ptr ) ( free( ptr ) )
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#include <assert.h>
|
||||||
|
#define MID_LOG(...) (void) __VA_ARGS__
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable: 4242 )
|
||||||
|
#pragma warning( disable: 4244 )
|
||||||
|
#pragma warning( disable: 4365 )
|
||||||
|
#pragma warning( disable: 4668 )
|
||||||
|
#pragma warning( disable: 4701 )
|
||||||
|
#pragma warning( disable: 4703 )
|
||||||
|
|
||||||
|
#ifndef MID_NO_TSF_IMPLEMENTATION
|
||||||
|
#define TSF_NO_STDIO
|
||||||
|
#define TSF_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
#include "3rd_tsf.h"
|
||||||
|
|
||||||
|
#pragma warning( disable: 4201 )
|
||||||
|
|
||||||
|
#ifndef MID_NO_TML_IMPLEMENTATION
|
||||||
|
#define TML_NO_STDIO
|
||||||
|
#define TML_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
#include "3rd_tml.h"
|
||||||
|
|
||||||
|
#pragma warning( pop )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MID_ENABLE_RAW
|
||||||
|
|
||||||
|
typedef struct mid_event_t
|
||||||
|
{
|
||||||
|
MID_U32 delay_us;
|
||||||
|
MID_U8 channel;
|
||||||
|
MID_U8 type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct { MID_U8 program; } program_change;
|
||||||
|
struct { MID_U8 note; MID_U8 velocity; } note_on;
|
||||||
|
struct { MID_U8 note; } note_off;
|
||||||
|
struct { MID_U8 key; MID_U8 key_pressure; } key_pressure;
|
||||||
|
struct { MID_U16 value; } pitch_bend;
|
||||||
|
struct { MID_U8 control, control_value; } control_change;
|
||||||
|
struct { MID_U8 channel_pressure; } channel_pressure;
|
||||||
|
} data;
|
||||||
|
} mid_event_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct mid_song_t
|
||||||
|
{
|
||||||
|
int event_count;
|
||||||
|
mid_event_t* events;
|
||||||
|
} mid_song_t;
|
||||||
|
|
||||||
|
|
||||||
|
struct mid_t
|
||||||
|
{
|
||||||
|
void* memctx;
|
||||||
|
mid_song_t song;
|
||||||
|
int percussion_preset;
|
||||||
|
MID_U64 playback_accumulated_time_us;
|
||||||
|
int playback_sample_pos;
|
||||||
|
int playback_event_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MID_ENABLE_RAW */
|
||||||
|
|
||||||
|
|
||||||
|
mid_t* mid_create( void const* midi_data, size_t midi_size, void* memctx )
|
||||||
|
{
|
||||||
|
tml_message* mid_file = tml_load_memory( midi_data, (int) midi_size );
|
||||||
|
if( !mid_file ) return NULL;
|
||||||
|
int count = 0;
|
||||||
|
tml_message* iter = mid_file;
|
||||||
|
while( iter )
|
||||||
|
{
|
||||||
|
if( iter->type == TML_PROGRAM_CHANGE || iter->type == TML_NOTE_ON || iter->type == TML_NOTE_OFF ||
|
||||||
|
iter->type == TML_PITCH_BEND || iter->type == TML_CONTROL_CHANGE )
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
iter = iter->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
mid_event_t* events = (mid_event_t*) malloc( sizeof( mid_event_t ) * count );
|
||||||
|
int events_count = 0;
|
||||||
|
unsigned int time = 0;
|
||||||
|
tml_message* msg = mid_file;
|
||||||
|
while( msg )
|
||||||
|
{
|
||||||
|
if( msg->type == TML_PROGRAM_CHANGE || msg->type == TML_NOTE_ON || msg->type == TML_NOTE_OFF ||
|
||||||
|
msg->type == TML_PITCH_BEND || msg->type == TML_CONTROL_CHANGE )
|
||||||
|
{
|
||||||
|
mid_event_t* event = &events[ events_count++ ];
|
||||||
|
event->delay_us = ( msg->time - time ) * 1000;
|
||||||
|
time = msg->time;
|
||||||
|
event->channel = msg->channel;
|
||||||
|
event->type = msg->type;
|
||||||
|
switch( msg->type )
|
||||||
|
{
|
||||||
|
case TML_PROGRAM_CHANGE:
|
||||||
|
event->data.program_change.program = (MID_U8) msg->program;
|
||||||
|
break;
|
||||||
|
case TML_NOTE_ON: //play a note
|
||||||
|
event->data.note_on.note = (MID_U8) msg->key;
|
||||||
|
event->data.note_on.velocity = (MID_U8) msg->velocity;
|
||||||
|
break;
|
||||||
|
case TML_NOTE_OFF: //stop a note
|
||||||
|
event->data.note_off.note = (MID_U8) msg->key;
|
||||||
|
break;
|
||||||
|
case TML_PITCH_BEND: //pitch wheel modification
|
||||||
|
event->data.pitch_bend.value = (MID_U16) msg->pitch_bend;
|
||||||
|
break;
|
||||||
|
case TML_CONTROL_CHANGE: //MIDI controller messages
|
||||||
|
event->data.control_change.control = (MID_U8) msg->control;
|
||||||
|
event->data.control_change.control_value = (MID_U8) msg->control_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = msg->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tml_free( mid_file );
|
||||||
|
|
||||||
|
mid_t* mid = (mid_t*) MID_MALLOC( memctx, sizeof( mid_t ) );
|
||||||
|
mid->memctx = memctx;
|
||||||
|
mid->song.event_count = events_count;
|
||||||
|
mid->song.events = events;
|
||||||
|
|
||||||
|
mid->playback_accumulated_time_us = 0ull;
|
||||||
|
mid->playback_sample_pos = 0;
|
||||||
|
mid->playback_event_pos = 0;
|
||||||
|
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mid_destroy( mid_t* mid )
|
||||||
|
{
|
||||||
|
if( mid->song.events ) MID_FREE( mid->memctx, mid->song.events );
|
||||||
|
MID_FREE( mid->memctx, mid );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int mid_init_raw( mid_t* mid, void const* raw_data, size_t raw_size )
|
||||||
|
{
|
||||||
|
int events_count = *(int*)raw_data;
|
||||||
|
if( sizeof( mid_event_t ) * events_count != raw_size - sizeof( int ) ) return 0;
|
||||||
|
|
||||||
|
mid->memctx = NULL;
|
||||||
|
|
||||||
|
mid->song.event_count = events_count;
|
||||||
|
mid->song.events = (mid_event_t*)( ( (int*)raw_data ) + 1 );
|
||||||
|
|
||||||
|
mid->playback_accumulated_time_us = 0ull;
|
||||||
|
mid->playback_sample_pos = 0;
|
||||||
|
mid->playback_event_pos = 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t mid_save_raw( mid_t* mid, void* data, size_t capacity )
|
||||||
|
{
|
||||||
|
size_t size = sizeof( mid_event_t ) * mid->song.event_count + sizeof( int );
|
||||||
|
if( data && capacity >= size )
|
||||||
|
{
|
||||||
|
*(int*)data = mid->song.event_count;
|
||||||
|
memcpy( ( (int*)data ) + 1, mid->song.events, sizeof( mid_event_t ) * mid->song.event_count );
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mid_skip_leading_silence( mid_t* mid, tsf* sound_font )
|
||||||
|
{
|
||||||
|
(void) sound_font;
|
||||||
|
for( ; ; )
|
||||||
|
{
|
||||||
|
MID_U64 next_event_delay_us = mid->song.events[ mid->playback_event_pos ].delay_us;
|
||||||
|
MID_U64 playback_time_us = ( mid->playback_sample_pos * 1000000ull ) / 44100ull;
|
||||||
|
MID_U64 next_event_time_us = mid->playback_accumulated_time_us + next_event_delay_us;
|
||||||
|
assert( next_event_time_us >= playback_time_us );
|
||||||
|
MID_U64 time_until_next_event = next_event_time_us - playback_time_us;
|
||||||
|
int samples_until_next_event = (int)( ( time_until_next_event * 44100ull ) / 1000000ull );
|
||||||
|
mid_event_t* event = &mid->song.events[ mid->playback_event_pos ];
|
||||||
|
switch( event->type )
|
||||||
|
{
|
||||||
|
case TML_PROGRAM_CHANGE:
|
||||||
|
tsf_channel_set_presetnumber( sound_font, event->channel, event->data.program_change.program, ( event->channel == 9 ) );
|
||||||
|
break;
|
||||||
|
case TML_NOTE_ON:
|
||||||
|
return;
|
||||||
|
case TML_NOTE_OFF: //stop a note
|
||||||
|
tsf_channel_note_off( sound_font, event->channel, event->data.note_off.note );
|
||||||
|
break;
|
||||||
|
case TML_PITCH_BEND: //pitch wheel modification
|
||||||
|
tsf_channel_set_pitchwheel( sound_font, event->channel, event->data.pitch_bend.value );
|
||||||
|
break;
|
||||||
|
case TML_CONTROL_CHANGE: //MIDI controller messages
|
||||||
|
tsf_channel_midi_control( sound_font, event->channel, event->data.control_change.control, event->data.control_change.control_value );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mid->playback_sample_pos += samples_until_next_event;
|
||||||
|
mid->playback_accumulated_time_us += next_event_delay_us;
|
||||||
|
mid->playback_event_pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int mid_render_short( mid_t* mid, short* sample_pairs, int sample_pairs_count, tsf* sound_font )
|
||||||
|
{
|
||||||
|
int samples_rendered = 0;
|
||||||
|
memset( sample_pairs, 0, sample_pairs_count * sizeof( short ) * 2 );
|
||||||
|
while( samples_rendered < sample_pairs_count )
|
||||||
|
{
|
||||||
|
MID_U64 next_event_delay_us = mid->song.events[ mid->playback_event_pos ].delay_us;
|
||||||
|
MID_U64 playback_time_us = ( mid->playback_sample_pos * 1000000ull ) / 44100ull;
|
||||||
|
MID_U64 next_event_time_us = mid->playback_accumulated_time_us + next_event_delay_us;
|
||||||
|
assert( next_event_time_us >= playback_time_us );
|
||||||
|
MID_U64 time_until_next_event = next_event_time_us - playback_time_us;
|
||||||
|
int samples_until_next_event = (int)( ( time_until_next_event * 44100ull ) / 1000000ull );
|
||||||
|
int samples_to_render = samples_until_next_event;
|
||||||
|
if( samples_to_render > sample_pairs_count - samples_rendered )
|
||||||
|
{
|
||||||
|
samples_to_render = sample_pairs_count - samples_rendered;
|
||||||
|
tsf_render_short( sound_font, sample_pairs + samples_rendered * 2,
|
||||||
|
samples_to_render, 1 );
|
||||||
|
samples_rendered += samples_to_render;
|
||||||
|
mid->playback_sample_pos += samples_to_render;
|
||||||
|
return samples_rendered;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tsf_render_short( sound_font, sample_pairs + samples_rendered * 2,
|
||||||
|
samples_to_render, 1 );
|
||||||
|
samples_rendered += samples_to_render;
|
||||||
|
mid->playback_sample_pos += samples_to_render;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mid->playback_accumulated_time_us += next_event_delay_us;
|
||||||
|
mid_event_t* event = &mid->song.events[ mid->playback_event_pos++ ];
|
||||||
|
switch( event->type )
|
||||||
|
{
|
||||||
|
case TML_PROGRAM_CHANGE:
|
||||||
|
tsf_channel_set_presetnumber( sound_font, event->channel, event->data.program_change.program, ( event->channel == 9 ) );
|
||||||
|
break;
|
||||||
|
case TML_NOTE_ON:
|
||||||
|
tsf_channel_note_on( sound_font, event->channel, event->data.note_on.note, event->data.note_on.velocity / 127.0f );
|
||||||
|
break;
|
||||||
|
case TML_NOTE_OFF: //stop a note
|
||||||
|
tsf_channel_note_off( sound_font, event->channel, event->data.note_off.note );
|
||||||
|
break;
|
||||||
|
case TML_PITCH_BEND: //pitch wheel modification
|
||||||
|
tsf_channel_set_pitchwheel( sound_font, event->channel, event->data.pitch_bend.value );
|
||||||
|
break;
|
||||||
|
case TML_CONTROL_CHANGE: //MIDI controller messages
|
||||||
|
tsf_channel_midi_control( sound_font, event->channel, event->data.control_change.control, event->data.control_change.control_value );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples_rendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MID_IMPLEMENTATION */
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
This software is available under 2 licenses - you may choose the one you like.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Mattias Gustavsson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
|
@ -0,0 +1,441 @@
|
||||||
|
/* sproutline - v0.10 - public domain sprite outline detector - http://github.org/ands/sproutline
|
||||||
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
|
Do this:
|
||||||
|
#define S2O_IMPLEMENTATION
|
||||||
|
before you include this file in *one* C or C++ file to create the implementation.
|
||||||
|
|
||||||
|
// i.e. it should look like this:
|
||||||
|
#include ...
|
||||||
|
#include ...
|
||||||
|
#include ...
|
||||||
|
#define S2O_IMPLEMENTATION
|
||||||
|
#include "sproutline.h"
|
||||||
|
|
||||||
|
You can #define S2O_MALLOC to avoid using malloc
|
||||||
|
|
||||||
|
|
||||||
|
QUICK NOTES:
|
||||||
|
Primarily of interest to game developers.
|
||||||
|
- Recommended to be used with stb_image.
|
||||||
|
- Detects outlines in sprite images with alpha channels.
|
||||||
|
- Extracts outlines as clockwise paths.
|
||||||
|
- Simplifies outlines based on a distance metric.
|
||||||
|
|
||||||
|
Full documentation under "DOCUMENTATION" below.
|
||||||
|
|
||||||
|
|
||||||
|
Revision 0.10 release notes:
|
||||||
|
|
||||||
|
- Initial release of sproutline.h.
|
||||||
|
|
||||||
|
- Added S2O_MALLOC macro for replacing the memory allocator.
|
||||||
|
Unlike most STB libraries, this macro doesn't support a context parameter,
|
||||||
|
so if you need to pass a context in to the allocator, you'll have to
|
||||||
|
store it in a global or a thread-local variable.
|
||||||
|
|
||||||
|
|
||||||
|
Revision history:
|
||||||
|
0.10 (2015-10-22) initial version
|
||||||
|
|
||||||
|
============================ Contributors =========================
|
||||||
|
|
||||||
|
Andreas Mantler (ands)
|
||||||
|
|
||||||
|
License:
|
||||||
|
This software is in the public domain. Where that dedication is not
|
||||||
|
recognized, you are granted a perpetual, irrevocable license to copy
|
||||||
|
and modify this file however you want.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef S2O_INCLUDE_SPROUTLINE_H
|
||||||
|
#define S2O_INCLUDE_SPROUTLINE_H
|
||||||
|
|
||||||
|
// DOCUMENTATION
|
||||||
|
//
|
||||||
|
// Limitations:
|
||||||
|
// - currently only works with images that have alpha channels
|
||||||
|
//
|
||||||
|
// Basic usage (with stb_image):
|
||||||
|
// int w, h, n, l;
|
||||||
|
// unsigned char *rgba = stbi_load(filename, &w, &h, &n, 4);
|
||||||
|
// unsigned char *alpha = s2o_rgba_to_alpha(rgba, w, h);
|
||||||
|
// unsigned char *thresholded = s2o_alpha_to_thresholded(alpha, w, h, ALPHA_THRESHOLD);
|
||||||
|
// unsigned char *outlined = s2o_thresholded_to_outlined(thresholded, w, h);
|
||||||
|
// s2o_point *outline = s2o_extract_outline_path(outlined, w, h, &l, 0);
|
||||||
|
// while(l)
|
||||||
|
// {
|
||||||
|
// s2o_distance_based_path_simplification(outline, &l, DISTANCE_THRESHOLD);
|
||||||
|
// // ... process outline here ...
|
||||||
|
// // ... l = number of points in outline
|
||||||
|
// // ... ALPHA_THRESHOLD = 1..255 (the min value to be considered solid)
|
||||||
|
// // ... DISTANCE_THRESHOLD = 0.0f..Inf (~0.5f is a suitable value)
|
||||||
|
// // ... a greater value results in fewer points in the output
|
||||||
|
//
|
||||||
|
// outline = s2o_extract_outline_path(outlined, w, h, &l, outline);
|
||||||
|
// };
|
||||||
|
// free(outline);
|
||||||
|
// free(outlined);
|
||||||
|
// free(thresholded);
|
||||||
|
// free(alpha);
|
||||||
|
// free(rgba);
|
||||||
|
//
|
||||||
|
// s2o_rgba_to_alpha:
|
||||||
|
// Expects an 'unsigned char *' to memory of w * h 4-byte pixels in 'RGBA' order.
|
||||||
|
// The return value is an 'unsigned char *' to memory of w * h 1-byte pixel alpha components.
|
||||||
|
//
|
||||||
|
// s2o_alpha_to_thresholded:
|
||||||
|
// Expects an 'unsigned char *' to memory of w * h 1-byte pixel alpha components.
|
||||||
|
// The return value is an 'unsigned char *' to memory of w * h 1-byte values
|
||||||
|
// that are 255 if the corresponding input is >= the specified threshold, otherwise 0.
|
||||||
|
//
|
||||||
|
// s2o_thresholded_to_outlined:
|
||||||
|
// Expects an 'unsigned char *' to memory of w * h 1-byte pixels indicating their solidity {0, nonzero}.
|
||||||
|
// The return value is an 'unsigned char *' to memory of w * h 1-byte pixels that indicate if the
|
||||||
|
// corresponding input value is part of an outline (= is solid and has a non-solid neighbour).
|
||||||
|
//
|
||||||
|
// s2o_extract_outline_path:
|
||||||
|
// Expects an 'unsigned char *' to memory of w * h 1-byte pixels indicating their outline membership.
|
||||||
|
// The return value is an 's2o_point *' to memory of l s2o_point values consisting of a short x and y value.
|
||||||
|
// The procedure scans the input data from top to bottom and starts extracting the first outline it finds.
|
||||||
|
// The pixels corresponding to the extracted outline are set to 0 in the input, so that a subsequent call to
|
||||||
|
// s2o_extract_outline_path extracts a different outline.
|
||||||
|
// The length is set to 0 if no outline was found.
|
||||||
|
//
|
||||||
|
// s2o_distance_based_path_simplification:
|
||||||
|
// Expects an 's2o_point *' to memory of l outline points.
|
||||||
|
// The procedure throws out points in place that lie on or close to linear sections of the outline.
|
||||||
|
// The distanceThreshold parameter specifies the min distance value for points to remain in the outline.
|
||||||
|
//
|
||||||
|
// ===========================================================================
|
||||||
|
//
|
||||||
|
// Philosophy
|
||||||
|
//
|
||||||
|
// This library is designed with the stb philosophy in mind.
|
||||||
|
// stb libraries are designed with the following priorities:
|
||||||
|
//
|
||||||
|
// 1. easy to use
|
||||||
|
// 2. easy to maintain
|
||||||
|
// 3. good performance
|
||||||
|
//
|
||||||
|
// Some secondary priorities arise directly from the first two, some of which
|
||||||
|
// make more explicit reasons why performance can't be emphasized.
|
||||||
|
//
|
||||||
|
// - Portable ("ease of use")
|
||||||
|
// - Small footprint ("easy to maintain")
|
||||||
|
// - No dependencies ("ease of use")
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef unsigned char s2o_uc;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef S2O_STATIC
|
||||||
|
#define S2ODEF static
|
||||||
|
#else
|
||||||
|
#define S2ODEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// PRIMARY API
|
||||||
|
//
|
||||||
|
|
||||||
|
S2ODEF s2o_uc * s2o_rgba_to_alpha (const s2o_uc *data, int w, int h);
|
||||||
|
S2ODEF s2o_uc * s2o_alpha_to_thresholded (const s2o_uc *data, int w, int h, s2o_uc threshold);
|
||||||
|
S2ODEF s2o_uc * s2o_thresholded_to_outlined(const s2o_uc *data, int w, int h);
|
||||||
|
|
||||||
|
typedef struct { short x, y; } s2o_point;
|
||||||
|
S2ODEF s2o_point * s2o_extract_outline_path(s2o_uc *data, int w, int h, int *point_count, s2o_point *reusable_outline);
|
||||||
|
S2ODEF void s2o_distance_based_path_simplification(s2o_point *outline, int *outline_length, float distance_threshold);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//// end header file /////////////////////////////////////////////////////
|
||||||
|
#endif // S2O_INCLUDE_SPROUTLINE_H
|
||||||
|
|
||||||
|
#ifdef S2O_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <math.h> // sqrtf, abs
|
||||||
|
|
||||||
|
#ifndef S2O_MALLOC
|
||||||
|
#include <stdlib.h> // malloc
|
||||||
|
#define S2O_MALLOC(sz) malloc(sz)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// locally used types
|
||||||
|
|
||||||
|
typedef int s2o_bool;
|
||||||
|
|
||||||
|
// 2d point type helpers
|
||||||
|
#define S2O_POINT_ADD(result, a, b) { (result).x = (a).x + (b).x; (result).y = (a).y + (b).y; }
|
||||||
|
#define S2O_POINT_SUB(result, a, b) { (result).x = (a).x - (b).x; (result).y = (a).y - (b).y; }
|
||||||
|
#define S2O_POINT_IS_INSIDE(a, w, h) ((a).x >= 0 && (a).y >= 0 && (a).x < (w) && (a).y < (h))
|
||||||
|
#define S2O_POINT_IS_NEXT_TO(a, b) ((a).x - (b).x <= 1 && (a).x - (b).x >= -1 && (a).y - (b).y <= 1 && (a).y - (b).y >= -1)
|
||||||
|
|
||||||
|
// direction type
|
||||||
|
typedef int s2o_direction; // 8 cw directions: >, _|, v, |_, <, |", ^, "|
|
||||||
|
#define S2O_DIRECTION_OPPOSITE(dir) ((dir + 4) & 7)
|
||||||
|
static const s2o_point s2o_direction_to_pixel_offset[] = { {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1} };
|
||||||
|
|
||||||
|
// image manipulation functions
|
||||||
|
S2ODEF s2o_uc * s2o_rgba_to_alpha(const s2o_uc *data, int w, int h)
|
||||||
|
{
|
||||||
|
s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h);
|
||||||
|
int x, y;
|
||||||
|
for (y = 0; y < h; y++)
|
||||||
|
for (x = 0; x < w; x++)
|
||||||
|
result[y * w + x] = data[(y * w + x) * 4 + 3];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2ODEF s2o_uc * s2o_alpha_to_thresholded(const s2o_uc *data, int w, int h, s2o_uc threshold)
|
||||||
|
{
|
||||||
|
s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h);
|
||||||
|
int x, y;
|
||||||
|
for (y = 0; y < h; y++)
|
||||||
|
for (x = 0; x < w; x++)
|
||||||
|
result[y * w + x] = data[y * w + x] >= threshold ? 255 : 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2ODEF s2o_uc * s2o_dilate_thresholded(const s2o_uc *data, int w, int h)
|
||||||
|
{
|
||||||
|
int x, y, dx, dy, cx, cy;
|
||||||
|
s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h);
|
||||||
|
for (y = 0; y < h; y++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
result[y * w + x] = 0;
|
||||||
|
for (dy = -1; dy <= 1; dy++)
|
||||||
|
{
|
||||||
|
for (dx = -1; dx <= 1; dx++)
|
||||||
|
{
|
||||||
|
cx = x + dx;
|
||||||
|
cy = y + dy;
|
||||||
|
if (cx >= 0 && cx < w && cy >= 0 && cy < h)
|
||||||
|
{
|
||||||
|
if (data[cy * w + cx])
|
||||||
|
{
|
||||||
|
result[y * w + x] = 255;
|
||||||
|
dy = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2ODEF s2o_uc * s2o_thresholded_to_outlined(const s2o_uc *data, int w, int h)
|
||||||
|
{
|
||||||
|
s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h);
|
||||||
|
int x, y;
|
||||||
|
for (x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
result[x] = data[x];
|
||||||
|
result[(h - 1) * w + x] = data[(h - 1) * w + x];
|
||||||
|
}
|
||||||
|
for (y = 1; y < h - 1; y++)
|
||||||
|
{
|
||||||
|
result[y * w] = data[y * w];
|
||||||
|
for (x = 1; x < w - 1; x++)
|
||||||
|
{
|
||||||
|
if (data[y * w + x] &&
|
||||||
|
(
|
||||||
|
!data[y * w + x - 1] ||
|
||||||
|
!data[y * w + x + 1] ||
|
||||||
|
!data[y * w + x - w] ||
|
||||||
|
!data[y * w + x + w]
|
||||||
|
))
|
||||||
|
{
|
||||||
|
result[y * w + x] = 255;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result[y * w + x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[y * w + w - 1] = data[y * w + w - 1];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// outline path procedures
|
||||||
|
static s2o_bool s2o_find_first_filled_pixel(const s2o_uc *data, int w, int h, s2o_point *first)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
for (y = 0; y < h; y++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
if (data[y * w + x])
|
||||||
|
{
|
||||||
|
first->x = (short)x;
|
||||||
|
first->y = (short)y;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s2o_bool s2o_find_next_filled_pixel(const s2o_uc *data, int w, int h, s2o_point current, s2o_direction *dir, s2o_point *next)
|
||||||
|
{
|
||||||
|
// turn around 180°, then make a clockwise scan for a filled pixel
|
||||||
|
*dir = S2O_DIRECTION_OPPOSITE(*dir);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
S2O_POINT_ADD(*next, current, s2o_direction_to_pixel_offset[*dir]);
|
||||||
|
|
||||||
|
if (S2O_POINT_IS_INSIDE(*next, w, h) && data[next->y * w + next->x])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// move to next angle (clockwise)
|
||||||
|
*dir = *dir - 1;
|
||||||
|
if (*dir < 0)
|
||||||
|
*dir = 7;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2ODEF s2o_point * s2o_extract_outline_path(s2o_uc *data, int w, int h, int *point_count, s2o_point *reusable_outline)
|
||||||
|
{
|
||||||
|
s2o_point *outline = reusable_outline;
|
||||||
|
if (!outline)
|
||||||
|
outline = (s2o_point*)S2O_MALLOC(w * h * sizeof(s2o_point));
|
||||||
|
|
||||||
|
s2o_point current, next;
|
||||||
|
|
||||||
|
restart:
|
||||||
|
if (!s2o_find_first_filled_pixel(data, w, h, ¤t))
|
||||||
|
{
|
||||||
|
*point_count = 0;
|
||||||
|
return outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
s2o_direction dir = 0;
|
||||||
|
|
||||||
|
while(S2O_POINT_IS_INSIDE(current, w, h) && count < (w*h)) //< @r-lyeh: buffer overflow: add count<w*h
|
||||||
|
{
|
||||||
|
data[current.y * w + current.x] = 0; // clear the visited path
|
||||||
|
outline[count++] = current; // add our current point to the outline
|
||||||
|
if (!s2o_find_next_filled_pixel(data, w, h, current, &dir, &next))
|
||||||
|
{
|
||||||
|
// find loop connection
|
||||||
|
s2o_bool found = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < count / 2; i++) // only allow big loops
|
||||||
|
{
|
||||||
|
if (S2O_POINT_IS_NEXT_TO(current, outline[i]))
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// go backwards until we see outline pixels again
|
||||||
|
dir = S2O_DIRECTION_OPPOSITE(dir);
|
||||||
|
count--; // back up
|
||||||
|
int prev;
|
||||||
|
for(prev = count; prev >= 0 && count < (w * h); prev--) //< @r-lyeh: buffer overflow: add count<w*h
|
||||||
|
{
|
||||||
|
current = outline[prev];
|
||||||
|
outline[count++] = current; // add our current point to the outline again
|
||||||
|
if (s2o_find_next_filled_pixel(data, w, h, current, &dir, &next))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 2) // too small, discard and try again!
|
||||||
|
goto restart;
|
||||||
|
*point_count = count;
|
||||||
|
return outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2ODEF void s2o_distance_based_path_simplification(s2o_point *outline, int *outline_length, float distance_threshold)
|
||||||
|
{
|
||||||
|
int length = *outline_length;
|
||||||
|
int l;
|
||||||
|
for (l = length / 2 /*length - 1*/; l > 1; l--)
|
||||||
|
{
|
||||||
|
int a, b = l;
|
||||||
|
for (a = 0; a < length; a++)
|
||||||
|
{
|
||||||
|
s2o_point ab;
|
||||||
|
S2O_POINT_SUB(ab, outline[b], outline[a]);
|
||||||
|
float lab = sqrtf((float)(ab.x * ab.x + ab.y * ab.y));
|
||||||
|
float ilab = 1.0f / lab;
|
||||||
|
float abnx = ab.x * ilab, abny = ab.y * ilab;
|
||||||
|
|
||||||
|
if (lab != 0.0f)
|
||||||
|
{
|
||||||
|
s2o_bool found = 1;
|
||||||
|
int i = (a + 1) % length;
|
||||||
|
while (i != b)
|
||||||
|
{
|
||||||
|
s2o_point ai;
|
||||||
|
S2O_POINT_SUB(ai, outline[i], outline[a]);
|
||||||
|
float t = (abnx * ai.x + abny * ai.y) * ilab;
|
||||||
|
float distance = -abny * ai.x + abnx * ai.y;
|
||||||
|
if (t < 0.0f || t > 1.0f || distance > distance_threshold || -distance > distance_threshold)
|
||||||
|
{
|
||||||
|
found = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++i == length)
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (a < b)
|
||||||
|
{
|
||||||
|
for (i = 0; i < length - b; i++)
|
||||||
|
outline[a + i + 1] = outline[b + i];
|
||||||
|
length -= b - a - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
length = a - b + 1;
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
outline[i] = outline[b + i];
|
||||||
|
}
|
||||||
|
if (l >= length)
|
||||||
|
l = length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++b >= length)
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*outline_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // S2O_IMPLEMENTATION
|
|
@ -126,6 +126,8 @@
|
||||||
|
|
||||||
{{FILE:v4k_font.c}}
|
{{FILE:v4k_font.c}}
|
||||||
|
|
||||||
|
{{FILE:v4k_gui.c}}
|
||||||
|
|
||||||
{{FILE:v4k_input.c}}
|
{{FILE:v4k_input.c}}
|
||||||
|
|
||||||
{{FILE:v4k_math.c}}
|
{{FILE:v4k_math.c}}
|
||||||
|
|
|
@ -145,6 +145,7 @@ extern "C" {
|
||||||
{{FILE:v4k_string.h}}
|
{{FILE:v4k_string.h}}
|
||||||
|
|
||||||
{{FILE:v4k_sprite.h}}
|
{{FILE:v4k_sprite.h}}
|
||||||
|
{{FILE:v4k_gui.h}}
|
||||||
|
|
||||||
{{FILE:v4k_system.h}}
|
{{FILE:v4k_system.h}}
|
||||||
|
|
||||||
|
|
|
@ -507,22 +507,31 @@ void editor_pump() {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
API void editor_cursorpos(int x, int y);
|
void editor_setmouse(int x, int y) {
|
||||||
void editor_cursorpos(int x, int y) {
|
|
||||||
glfwSetCursorPos( window_handle(), x, y );
|
glfwSetCursorPos( window_handle(), x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_symbol(int x, int y, const char *sym) {
|
vec2 editor_glyph(int x, int y, unsigned cp) {
|
||||||
// style: atlas size, unicode ranges and 6 font faces max
|
// 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_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}},
|
||||||
// style: 10 colors max
|
// style: 10 colors max
|
||||||
do_once font_color(FONT_COLOR1, WHITE);
|
do_once font_color(FONT_COLOR1, WHITE);
|
||||||
do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY);
|
do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY);
|
||||||
do_once font_color(FONT_COLOR3, YELLOW);
|
do_once font_color(FONT_COLOR3, YELLOW);
|
||||||
do_once font_color(FONT_COLOR4, ORANGE);
|
do_once font_color(FONT_COLOR4, ORANGE);
|
||||||
do_once font_color(FONT_COLOR5, CYAN);
|
do_once font_color(FONT_COLOR5, CYAN);
|
||||||
|
const char *sym = codepoint_to_utf8(cp);
|
||||||
font_goto(x,y);
|
font_goto(x,y);
|
||||||
font_print(va(FONT_FACE2 /*FONT_WHITE*/ FONT_H1 "%s", sym));
|
return font_print(va("%s" FONT_H1 "%s", cp >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 editor_glyphstr(int x, int y, const char *utf8) {
|
||||||
|
vec2 dim = {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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_frame( void (*game)(unsigned, float, double) ) {
|
void editor_frame( void (*game)(unsigned, float, double) ) {
|
||||||
|
|
|
@ -89,7 +89,9 @@ API void editor_inspect(obj *o);
|
||||||
API vec3 editor_pick(float mouse_x, float mouse_y);
|
API vec3 editor_pick(float mouse_x, float mouse_y);
|
||||||
API char* editor_path(const char *path);
|
API char* editor_path(const char *path);
|
||||||
|
|
||||||
API void editor_symbol(int x, int y, const char *sym);
|
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 void editor_gizmos(int dim);
|
API void editor_gizmos(int dim);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui (utils)
|
||||||
|
|
||||||
|
API vec2i draw_window_ui();
|
||||||
|
|
||||||
|
API void draw_rect(int rgba, vec2 start, vec2 end );
|
||||||
|
API void draw_rect_tex( texture_t texture, int rgba, vec2 start, vec2 end );
|
||||||
|
API void draw_rect_sheet( texture_t spritesheet, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end );
|
||||||
|
|
||||||
|
#define draw_rect_borders(color, x, y, w, h, borderWeight) do { \
|
||||||
|
int x1 = (x); \
|
||||||
|
int y1 = (y); \
|
||||||
|
int x2 = (x) + (w) - 1; \
|
||||||
|
int y2 = (y) + (h) - 1; \
|
||||||
|
draw_rect(color, vec2(x1, y1), vec2(x2, y1 + (borderWeight) - 1)); \
|
||||||
|
draw_rect(color, vec2(x1, y1), vec2(x1 + (borderWeight) - 1, y2)); \
|
||||||
|
draw_rect(color, vec2(x1, y2 - (borderWeight) + 1), vec2(x2, y2)); \
|
||||||
|
draw_rect(color, vec2(x2 - (borderWeight) + 1, y1), vec2(x2, y2)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
// #define lay_draw_rect(rgba, rect) draw_rect(rgba, vec2(rect.e[0], rect.e[1]), vec2(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
// #define lay_draw_rect_borders(rgba, rect, borderWeight) draw_rect_borders(rgba, rect.e[0], rect.e[1], rect.e[2], rect.e[3], borderWeight)
|
||||||
|
// #define lay_draw_rect_tex(tex, rgba, rect) draw_rect_tex(tex, rgba, vec2(rect.e[0], rect.e[1]), vec2(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
// #define l2m(rect) (vec4(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
#define v42v2(rect) vec2(rect.x,rect.y), vec2(rect.z,rect.w)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vec2i draw_window_ui() {
|
||||||
|
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||||
|
int w = window_width();
|
||||||
|
int h = window_height();
|
||||||
|
return vec2i(w/dpi.x, h/dpi.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect_sheet( texture_t texture, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end ) {
|
||||||
|
float gamma = 1;
|
||||||
|
static int program = -1, vbo = -1, vao = -1, u_inv_gamma = -1, u_tint = -1, u_has_tex = -1, u_window_width = -1, u_window_height = -1;
|
||||||
|
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||||
|
if( program < 0 ) {
|
||||||
|
const char* vs = vfs_read("shaders/rect_2d.vs");
|
||||||
|
const char* fs = vfs_read("shaders/rect_2d.fs");
|
||||||
|
|
||||||
|
program = shader(vs, fs, "", "fragcolor" , NULL);
|
||||||
|
ASSERT(program > 0);
|
||||||
|
u_inv_gamma = glGetUniformLocation(program, "u_inv_gamma");
|
||||||
|
u_tint = glGetUniformLocation(program, "u_tint");
|
||||||
|
u_has_tex = glGetUniformLocation(program, "u_has_tex");
|
||||||
|
u_window_width = glGetUniformLocation(program, "u_window_width");
|
||||||
|
u_window_height = glGetUniformLocation(program, "u_window_height");
|
||||||
|
glGenVertexArrays( 1, (GLuint*)&vao );
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = mul2(start, dpi);
|
||||||
|
end = mul2(end, dpi);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendEquation(GL_FUNC_ADD);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
GLenum texture_type = texture.flags & TEXTURE_ARRAY ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
||||||
|
// glEnable( GL_BLEND );
|
||||||
|
glUseProgram( program );
|
||||||
|
glUniform1f( u_inv_gamma, 1.0f / (gamma + !gamma) );
|
||||||
|
|
||||||
|
glBindVertexArray( vao );
|
||||||
|
|
||||||
|
glActiveTexture( GL_TEXTURE0 );
|
||||||
|
glBindTexture( texture_type, texture.id );
|
||||||
|
|
||||||
|
glUniform1i(u_has_tex, (texture.id != 0));
|
||||||
|
glUniform1f(u_window_width, (float)window_width());
|
||||||
|
glUniform1f(u_window_height, (float)window_height());
|
||||||
|
|
||||||
|
vec4 rgbaf = {((rgba>>24)&255)/255.f, ((rgba>>16)&255)/255.f,((rgba>>8)&255)/255.f,((rgba>>0)&255)/255.f};
|
||||||
|
glUniform4fv(u_tint, GL_TRUE, &rgbaf.x);
|
||||||
|
|
||||||
|
// normalize texture regions
|
||||||
|
if (texture.id != 0) {
|
||||||
|
tex_start.x /= texture.w;
|
||||||
|
tex_start.y /= texture.h;
|
||||||
|
tex_end.x /= texture.w;
|
||||||
|
tex_end.y /= texture.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLfloat vertices[] = {
|
||||||
|
// Positions // UVs
|
||||||
|
start.x, start.y, tex_start.x, tex_start.y,
|
||||||
|
end.x, start.y, tex_end.x, tex_start.y,
|
||||||
|
end.x, end.y, tex_end.x, tex_end.y,
|
||||||
|
start.x, start.y, tex_start.x, tex_start.y,
|
||||||
|
end.x, end.y, tex_end.x, tex_end.y,
|
||||||
|
start.x, end.y, tex_start.x, tex_end.y
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
|
||||||
|
|
||||||
|
glDrawArrays( GL_TRIANGLES, 0, 6 );
|
||||||
|
profile_incstat("Render.num_drawcalls", +1);
|
||||||
|
profile_incstat("Render.num_triangles", +2);
|
||||||
|
|
||||||
|
glBindTexture( texture_type, 0 );
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
glDisableVertexAttribArray(1);
|
||||||
|
glBindVertexArray( 0 );
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
glUseProgram( 0 );
|
||||||
|
// glDisable( GL_BLEND );
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect_tex( texture_t texture, int rgba, vec2 start, vec2 end ) {
|
||||||
|
draw_rect_sheet(texture, vec2(0, 0), vec2(texture.w, texture.h), rgba, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect(int rgba, vec2 start, vec2 end ) {
|
||||||
|
draw_rect_tex((texture_t){0}, rgba, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui
|
||||||
|
|
||||||
|
static __thread array(guiskin_t) skins=0;
|
||||||
|
static __thread guiskin_t *last_skin=0;
|
||||||
|
static __thread map(int, gui_state_t) ctl_states=0; //@leak
|
||||||
|
|
||||||
|
void gui_pushskin(guiskin_t skin) {
|
||||||
|
array_push(skins, skin);
|
||||||
|
last_skin = array_back(skins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_popskin() {
|
||||||
|
if (!last_skin) return;
|
||||||
|
if (last_skin->free) last_skin->free(last_skin->userdata);
|
||||||
|
array_pop(skins);
|
||||||
|
last_skin = array_count(skins) ? array_back(skins) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *gui_userdata() {
|
||||||
|
return last_skin->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
gui_state_t *gui_getstate(int id, int kind) {
|
||||||
|
if (!ctl_states) map_init(ctl_states, less_int, hash_int);
|
||||||
|
static gui_state_t st={0};
|
||||||
|
st.kind=kind;
|
||||||
|
return map_find_or_add(ctl_states, id, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool (gui_button)(int id, vec4 r, const char *skin) {
|
||||||
|
gui_state_t *entry = gui_getstate(id, GUI_BUTTON);
|
||||||
|
bool was_clicked=0;
|
||||||
|
entry->hover = false;
|
||||||
|
|
||||||
|
if (input(MOUSE_X) > r.x && input(MOUSE_X) < r.z && input(MOUSE_Y) > r.y && input(MOUSE_Y) < r.w) {
|
||||||
|
if (input_up(MOUSE_L) && entry->held) {
|
||||||
|
was_clicked=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->held = input_held(MOUSE_L);
|
||||||
|
entry->hover = true;
|
||||||
|
}
|
||||||
|
else if (input_up(MOUSE_L) && entry->held) {
|
||||||
|
entry->held = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, *entry, skin, r);
|
||||||
|
else {
|
||||||
|
draw_rect(entry->held ? 0x111111FF : entry->hover ? 0xEEEEEEFF : 0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return was_clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (gui_panel)(int id, vec4 r, const char *skin) {
|
||||||
|
gui_state_t *entry = gui_getstate(id, GUI_PANEL);
|
||||||
|
if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, *entry, skin?skin:"panel", r);
|
||||||
|
else {
|
||||||
|
draw_rect(0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skinned */
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_free(void* userdata) {
|
||||||
|
skinned_t *a = C_CAST(skinned_t*, userdata);
|
||||||
|
atlas_destroy(&a->atlas);
|
||||||
|
FREE(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
atlas_slice_frame_t *skinned_getsliceframe(atlas_t *a, const char *name, const char *fallback) {
|
||||||
|
#define atlas_loop(n)\
|
||||||
|
for (int i = 0; i < array_count(a->slices); i++)\
|
||||||
|
if (!strcmp(quark_string(&a->db, a->slices[i].name), n))\
|
||||||
|
return &a->slice_frames[a->slices[i].frames[0]];
|
||||||
|
atlas_loop(name);
|
||||||
|
atlas_loop(fallback);
|
||||||
|
return NULL;
|
||||||
|
#undef atlas_loop
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_missing_rect(vec4 r) {
|
||||||
|
draw_rect_tex(texture_checker(), 0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) {
|
||||||
|
if (!f->has_9slice) {
|
||||||
|
draw_rect_sheet(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 outer = f->bounds;
|
||||||
|
vec4 core = f->core;
|
||||||
|
core.x += outer.x;
|
||||||
|
core.y += outer.y;
|
||||||
|
core.z += outer.x;
|
||||||
|
core.w += outer.y;
|
||||||
|
|
||||||
|
// Define the 9 slices
|
||||||
|
vec4 top_left_slice = {outer.x, outer.y, core.x, core.y};
|
||||||
|
vec4 top_middle_slice = {core.x, outer.y, core.z, core.y};
|
||||||
|
vec4 top_right_slice = {core.z, outer.y, outer.z, core.y};
|
||||||
|
|
||||||
|
vec4 middle_left_slice = {outer.x, core.y, core.x, core.w};
|
||||||
|
vec4 center_slice = core;
|
||||||
|
vec4 middle_right_slice = {core.z, core.y, outer.z, core.w};
|
||||||
|
|
||||||
|
vec4 bottom_left_slice = {outer.x, core.w, core.x, outer.w};
|
||||||
|
vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w};
|
||||||
|
vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w};
|
||||||
|
|
||||||
|
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};
|
||||||
|
vec4 bottom_right = {r.z - (outer.z - core.z) * scale, r.w - (outer.w - core.w) * scale, r.z, r.w};
|
||||||
|
|
||||||
|
vec4 top = {top_left.z, r.y, top_right.x, top_left.w};
|
||||||
|
vec4 bottom = {bottom_left.z, bottom_left.y, bottom_right.x, r.w};
|
||||||
|
vec4 left = {r.x, top_left.w, top_left.z, bottom_left.y};
|
||||||
|
vec4 right = {top_right.x, top_right.w, r.z, bottom_right.y};
|
||||||
|
|
||||||
|
vec4 center = {top_left.z, top_left.w, top_right.x, bottom_right.y};
|
||||||
|
|
||||||
|
draw_rect_sheet(a->tex, v42v2(center_slice), 0xFFFFFFFF, v42v2(center));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_left_slice), 0xFFFFFFFF, v42v2(top_left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_right_slice), 0xFFFFFFFF, v42v2(top_right));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_left_slice), 0xFFFFFFFF, v42v2(bottom_left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_right_slice), 0xFFFFFFFF, v42v2(bottom_right));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_middle_slice), 0xFFFFFFFF, v42v2(top));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_middle_slice), 0xFFFFFFFF, v42v2(bottom));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(middle_left_slice), 0xFFFFFFFF, v42v2(left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(middle_right_slice), 0xFFFFFFFF, v42v2(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_rect(void* userdata, gui_state_t state, const char *skin, vec4 r) {
|
||||||
|
skinned_t *a = C_CAST(skinned_t*, userdata);
|
||||||
|
|
||||||
|
switch (state.kind) {
|
||||||
|
case GUI_BUTTON: {
|
||||||
|
char *btn = va("%s%s", skin?skin:a->button, state.held?"_press":state.hover?"_hover":"");
|
||||||
|
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, btn, skin?skin:a->button);
|
||||||
|
if (!f) skinned_draw_missing_rect(r);
|
||||||
|
else skinned_draw_sprite(a->scale, &a->atlas, f, r);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_preset_skins(skinned_t *s) {
|
||||||
|
s->panel = "panel";
|
||||||
|
s->button = "button";
|
||||||
|
}
|
||||||
|
|
||||||
|
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.free = skinned_free;
|
||||||
|
skinned_preset_skins(a);
|
||||||
|
return skin;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GUI_PANEL,
|
||||||
|
GUI_BUTTON,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct gui_state_t {
|
||||||
|
int kind;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool held;
|
||||||
|
bool hover;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} gui_state_t;
|
||||||
|
|
||||||
|
typedef struct guiskin_t {
|
||||||
|
void (*draw_rect_func)(void* userdata, gui_state_t state, const char *skin, vec4 rect);
|
||||||
|
void (*free)(void* userdata);
|
||||||
|
void *userdata;
|
||||||
|
} guiskin_t;
|
||||||
|
|
||||||
|
API void gui_pushskin(guiskin_t skin);
|
||||||
|
API void* gui_userdata();
|
||||||
|
// --
|
||||||
|
API void gui_panel(int id, vec4 rect, const char *skin);
|
||||||
|
API bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
|
API void gui_popskin();
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
#define gui_panel(...) gui_panel(__LINE__, __VA_ARGS__)
|
||||||
|
#define gui_button(...) gui_button(__LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
|
// default skins
|
||||||
|
API guiskin_t gui_skinned(const char *inifile, float scale);
|
||||||
|
|
||||||
|
typedef struct skinned_t {
|
||||||
|
atlas_t atlas;
|
||||||
|
float scale;
|
||||||
|
// unsigned framenum;
|
||||||
|
|
||||||
|
//skins
|
||||||
|
char *panel;
|
||||||
|
char *button;
|
||||||
|
} skinned_t;
|
||||||
|
|
|
@ -343,6 +343,16 @@ array(uint32_t) string32( const char *utf8 ) {
|
||||||
return out[slot];
|
return out[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
||||||
|
static char s[4+1];
|
||||||
|
memset(s, 0, 5);
|
||||||
|
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
||||||
|
else if (c < 0x800) s[0] = 0xC0 | ((c >> 6) & 0x1F), s[1] = 0x80 | ( c & 0x3F), s[2] = 0;
|
||||||
|
else if (c < 0x10000) s[0] = 0xE0 | ((c >> 12) & 0x0F), s[1] = 0x80 | ((c >> 6) & 0x3F), s[2] = 0x80 | ( c & 0x3F), s[3] = 0;
|
||||||
|
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// quarks
|
// quarks
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,8 @@ API char* strjoin(array(char*) list, const char *separator);
|
||||||
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
||||||
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
||||||
|
|
||||||
|
API const char* codepoint_to_utf8(unsigned cp);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ## string interning (quarks)
|
// ## string interning (quarks)
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
|
|
|
@ -174,9 +174,9 @@ void* ui_handle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nk_config_custom_fonts() {
|
static void nk_config_custom_fonts() {
|
||||||
#define UI_ICON_MIN ICON_MIN_MD
|
#define UI_ICON_MIN ICON_MD_MIN
|
||||||
#define UI_ICON_MED ICON_MAX_16_MD
|
#define UI_ICON_MED ICON_MD_MAX_16
|
||||||
#define UI_ICON_MAX ICON_MAX_MD
|
#define UI_ICON_MAX ICON_MD_MAX
|
||||||
|
|
||||||
#define ICON_BARS ICON_MD_MENU
|
#define ICON_BARS ICON_MD_MENU
|
||||||
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
||||||
|
@ -207,7 +207,7 @@ static void nk_config_custom_fonts() {
|
||||||
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
||||||
} icons[] = {
|
} icons[] = {
|
||||||
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
||||||
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MIN_MDI*/, 0xF1CC7/*ICON_MAX_MDI*/, 0}},
|
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}},
|
||||||
};
|
};
|
||||||
for( int f = 0; f < countof(icons); ++f )
|
for( int f = 0; f < countof(icons); ++f )
|
||||||
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
||||||
|
|
28
engine/v4k
28
engine/v4k
|
@ -11695,11 +11695,11 @@ int gladLoadGL( GLADloadfunc load) {
|
||||||
#ifndef ICON_MD_H
|
#ifndef ICON_MD_H
|
||||||
#define ICON_MD_H
|
#define ICON_MD_H
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MD "MaterialIcons-Regular.ttf"
|
#define ICON_MD_FILENAME "MaterialIcons-Regular.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MD 0xe000
|
#define ICON_MD_MIN 0xe000
|
||||||
#define ICON_MAX_16_MD 0xf8ff
|
#define ICON_MD_MAX_16 0xf8ff
|
||||||
#define ICON_MAX_MD 0x10fffd
|
#define ICON_MD_MAX 0x10fffd
|
||||||
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
#define ICON_MD_10K "\xee\xa5\x91" // U+e951
|
||||||
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
#define ICON_MD_10MP "\xee\xa5\x92" // U+e952
|
||||||
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
#define ICON_MD_11MP "\xee\xa5\x93" // U+e953
|
||||||
|
@ -319807,11 +319807,11 @@ rpmalloc_linker_reference(void) {
|
||||||
// for use with https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf
|
// for use with https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define FONT_ICON_FILE_NAME_MDI "materialdesignicons-webfont.ttf"
|
#define ICON_MDI_FILENAME "materialdesignicons-webfont.ttf"
|
||||||
|
|
||||||
#define ICON_MIN_MDI 0xF68C
|
#define ICON_MDI_MIN 0xF68C
|
||||||
#define ICON_MAX_16_MDI 0xF68C
|
#define ICON_MDI_MAX_16 0xF68C
|
||||||
#define ICON_MAX_MDI 0xF1CC7
|
#define ICON_MDI_MAX 0xF1CC7
|
||||||
#define ICON_MDI_AB_TESTING "\xf3\xb0\x87\x89" // U+F01C9
|
#define ICON_MDI_AB_TESTING "\xf3\xb0\x87\x89" // U+F01C9
|
||||||
#define ICON_MDI_ABACUS "\xf3\xb1\x9b\xa0" // U+F16E0
|
#define ICON_MDI_ABACUS "\xf3\xb1\x9b\xa0" // U+F16E0
|
||||||
#define ICON_MDI_ABJAD_ARABIC "\xf3\xb1\x8c\xa8" // U+F1328
|
#define ICON_MDI_ABJAD_ARABIC "\xf3\xb1\x8c\xa8" // U+F1328
|
||||||
|
@ -327819,7 +327819,7 @@ int printi(int i) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* codepoint_to_utf8(unsigned c);
|
static const char* codepoint_to_utf8_(unsigned c);
|
||||||
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved || inputtext
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char buf[16];
|
char buf[16];
|
||||||
|
@ -327863,7 +327863,7 @@ int lt_poll_event(lua_State *L) { // init.lua > core.step() wakes on mousemoved
|
||||||
goto bottom;
|
goto bottom;
|
||||||
|
|
||||||
break; case GLEQ_CODEPOINT_INPUT:
|
break; case GLEQ_CODEPOINT_INPUT:
|
||||||
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8(e.codepoint));
|
rc += lt_emit_event(L, "textinput", "s", codepoint_to_utf8_(e.codepoint));
|
||||||
|
|
||||||
break; case GLEQ_BUTTON_PRESSED:
|
break; case GLEQ_BUTTON_PRESSED:
|
||||||
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
rc += lt_emit_event(L, "mousepressed", "sddd", lt_button_name(e.mouse.button), lt_mx, lt_my, printi(1 + clicks));
|
||||||
|
@ -328015,7 +328015,7 @@ struct RenFont {
|
||||||
|
|
||||||
static struct { int left, top, right, bottom; } lt_clip;
|
static struct { int left, top, right, bottom; } lt_clip;
|
||||||
|
|
||||||
static const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
static const char* codepoint_to_utf8_(unsigned c) { //< @r-lyeh
|
||||||
static char s[4+1];
|
static char s[4+1];
|
||||||
lt_memset(s, 0, 5);
|
lt_memset(s, 0, 5);
|
||||||
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
||||||
|
@ -328024,7 +328024,7 @@ static const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
||||||
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
static const char* utf8_to_codepoint_(const char *p, unsigned *dst) {
|
||||||
unsigned res, n;
|
unsigned res, n;
|
||||||
switch (*p & 0xf0) {
|
switch (*p & 0xf0) {
|
||||||
case 0xf0 : res = *p & 0x07; n = 3; break;
|
case 0xf0 : res = *p & 0x07; n = 3; break;
|
||||||
|
@ -328200,7 +328200,7 @@ int ren_get_font_width(RenFont *font, const char *text) {
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint_(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSet *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
||||||
x += g->xadvance;
|
x += g->xadvance;
|
||||||
|
@ -328305,7 +328305,7 @@ int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color)
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint_(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSet *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
||||||
rect.x = g->x0;
|
rect.x = g->x0;
|
||||||
|
|
335
engine/v4k.c
335
engine/v4k.c
|
@ -846,6 +846,16 @@ array(uint32_t) string32( const char *utf8 ) {
|
||||||
return out[slot];
|
return out[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* codepoint_to_utf8(unsigned c) { //< @r-lyeh
|
||||||
|
static char s[4+1];
|
||||||
|
memset(s, 0, 5);
|
||||||
|
/**/ if (c < 0x80) s[0] = c, s[1] = 0;
|
||||||
|
else if (c < 0x800) s[0] = 0xC0 | ((c >> 6) & 0x1F), s[1] = 0x80 | ( c & 0x3F), s[2] = 0;
|
||||||
|
else if (c < 0x10000) s[0] = 0xE0 | ((c >> 12) & 0x0F), s[1] = 0x80 | ((c >> 6) & 0x3F), s[2] = 0x80 | ( c & 0x3F), s[3] = 0;
|
||||||
|
else if (c < 0x110000) s[0] = 0xF0 | ((c >> 18) & 0x07), s[1] = 0x80 | ((c >> 12) & 0x3F), s[2] = 0x80 | ((c >> 6) & 0x3F), s[3] = 0x80 | (c & 0x3F), s[4] = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// quarks
|
// quarks
|
||||||
|
|
||||||
|
@ -1368,9 +1378,9 @@ void* ui_handle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nk_config_custom_fonts() {
|
static void nk_config_custom_fonts() {
|
||||||
#define UI_ICON_MIN ICON_MIN_MD
|
#define UI_ICON_MIN ICON_MD_MIN
|
||||||
#define UI_ICON_MED ICON_MAX_16_MD
|
#define UI_ICON_MED ICON_MD_MAX_16
|
||||||
#define UI_ICON_MAX ICON_MAX_MD
|
#define UI_ICON_MAX ICON_MD_MAX
|
||||||
|
|
||||||
#define ICON_BARS ICON_MD_MENU
|
#define ICON_BARS ICON_MD_MENU
|
||||||
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
||||||
|
@ -1401,7 +1411,7 @@ static void nk_config_custom_fonts() {
|
||||||
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
||||||
} icons[] = {
|
} icons[] = {
|
||||||
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
||||||
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MIN_MDI*/, 0xF1CC7/*ICON_MAX_MDI*/, 0}},
|
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}},
|
||||||
};
|
};
|
||||||
for( int f = 0; f < countof(icons); ++f )
|
for( int f = 0; f < countof(icons); ++f )
|
||||||
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
||||||
|
@ -10993,6 +11003,306 @@ vec2 font_rect(const char *str) {
|
||||||
}
|
}
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
|
#line 1 "v4k_gui.c"
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui (utils)
|
||||||
|
|
||||||
|
API vec2i draw_window_ui();
|
||||||
|
|
||||||
|
API void draw_rect(int rgba, vec2 start, vec2 end );
|
||||||
|
API void draw_rect_tex( texture_t texture, int rgba, vec2 start, vec2 end );
|
||||||
|
API void draw_rect_sheet( texture_t spritesheet, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end );
|
||||||
|
|
||||||
|
#define draw_rect_borders(color, x, y, w, h, borderWeight) do { \
|
||||||
|
int x1 = (x); \
|
||||||
|
int y1 = (y); \
|
||||||
|
int x2 = (x) + (w) - 1; \
|
||||||
|
int y2 = (y) + (h) - 1; \
|
||||||
|
draw_rect(color, vec2(x1, y1), vec2(x2, y1 + (borderWeight) - 1)); \
|
||||||
|
draw_rect(color, vec2(x1, y1), vec2(x1 + (borderWeight) - 1, y2)); \
|
||||||
|
draw_rect(color, vec2(x1, y2 - (borderWeight) + 1), vec2(x2, y2)); \
|
||||||
|
draw_rect(color, vec2(x2 - (borderWeight) + 1, y1), vec2(x2, y2)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
// #define lay_draw_rect(rgba, rect) draw_rect(rgba, vec2(rect.e[0], rect.e[1]), vec2(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
// #define lay_draw_rect_borders(rgba, rect, borderWeight) draw_rect_borders(rgba, rect.e[0], rect.e[1], rect.e[2], rect.e[3], borderWeight)
|
||||||
|
// #define lay_draw_rect_tex(tex, rgba, rect) draw_rect_tex(tex, rgba, vec2(rect.e[0], rect.e[1]), vec2(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
// #define l2m(rect) (vec4(rect.e[0]+rect.e[2], rect.e[1]+rect.e[3]))
|
||||||
|
#define v42v2(rect) vec2(rect.x,rect.y), vec2(rect.z,rect.w)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vec2i draw_window_ui() {
|
||||||
|
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||||
|
int w = window_width();
|
||||||
|
int h = window_height();
|
||||||
|
return vec2i(w/dpi.x, h/dpi.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect_sheet( texture_t texture, vec2 tex_start, vec2 tex_end, int rgba, vec2 start, vec2 end ) {
|
||||||
|
float gamma = 1;
|
||||||
|
static int program = -1, vbo = -1, vao = -1, u_inv_gamma = -1, u_tint = -1, u_has_tex = -1, u_window_width = -1, u_window_height = -1;
|
||||||
|
vec2 dpi = ifdef(osx, window_dpi(), vec2(1,1));
|
||||||
|
if( program < 0 ) {
|
||||||
|
const char* vs = vfs_read("shaders/rect_2d.vs");
|
||||||
|
const char* fs = vfs_read("shaders/rect_2d.fs");
|
||||||
|
|
||||||
|
program = shader(vs, fs, "", "fragcolor" , NULL);
|
||||||
|
ASSERT(program > 0);
|
||||||
|
u_inv_gamma = glGetUniformLocation(program, "u_inv_gamma");
|
||||||
|
u_tint = glGetUniformLocation(program, "u_tint");
|
||||||
|
u_has_tex = glGetUniformLocation(program, "u_has_tex");
|
||||||
|
u_window_width = glGetUniformLocation(program, "u_window_width");
|
||||||
|
u_window_height = glGetUniformLocation(program, "u_window_height");
|
||||||
|
glGenVertexArrays( 1, (GLuint*)&vao );
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = mul2(start, dpi);
|
||||||
|
end = mul2(end, dpi);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendEquation(GL_FUNC_ADD);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
GLenum texture_type = texture.flags & TEXTURE_ARRAY ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
||||||
|
// glEnable( GL_BLEND );
|
||||||
|
glUseProgram( program );
|
||||||
|
glUniform1f( u_inv_gamma, 1.0f / (gamma + !gamma) );
|
||||||
|
|
||||||
|
glBindVertexArray( vao );
|
||||||
|
|
||||||
|
glActiveTexture( GL_TEXTURE0 );
|
||||||
|
glBindTexture( texture_type, texture.id );
|
||||||
|
|
||||||
|
glUniform1i(u_has_tex, (texture.id != 0));
|
||||||
|
glUniform1f(u_window_width, (float)window_width());
|
||||||
|
glUniform1f(u_window_height, (float)window_height());
|
||||||
|
|
||||||
|
vec4 rgbaf = {((rgba>>24)&255)/255.f, ((rgba>>16)&255)/255.f,((rgba>>8)&255)/255.f,((rgba>>0)&255)/255.f};
|
||||||
|
glUniform4fv(u_tint, GL_TRUE, &rgbaf.x);
|
||||||
|
|
||||||
|
// normalize texture regions
|
||||||
|
if (texture.id != 0) {
|
||||||
|
tex_start.x /= texture.w;
|
||||||
|
tex_start.y /= texture.h;
|
||||||
|
tex_end.x /= texture.w;
|
||||||
|
tex_end.y /= texture.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLfloat vertices[] = {
|
||||||
|
// Positions // UVs
|
||||||
|
start.x, start.y, tex_start.x, tex_start.y,
|
||||||
|
end.x, start.y, tex_end.x, tex_start.y,
|
||||||
|
end.x, end.y, tex_end.x, tex_end.y,
|
||||||
|
start.x, start.y, tex_start.x, tex_start.y,
|
||||||
|
end.x, end.y, tex_end.x, tex_end.y,
|
||||||
|
start.x, end.y, tex_start.x, tex_end.y
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
|
||||||
|
|
||||||
|
glDrawArrays( GL_TRIANGLES, 0, 6 );
|
||||||
|
profile_incstat("Render.num_drawcalls", +1);
|
||||||
|
profile_incstat("Render.num_triangles", +2);
|
||||||
|
|
||||||
|
glBindTexture( texture_type, 0 );
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
glDisableVertexAttribArray(1);
|
||||||
|
glBindVertexArray( 0 );
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
glUseProgram( 0 );
|
||||||
|
// glDisable( GL_BLEND );
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect_tex( texture_t texture, int rgba, vec2 start, vec2 end ) {
|
||||||
|
draw_rect_sheet(texture, vec2(0, 0), vec2(texture.w, texture.h), rgba, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect(int rgba, vec2 start, vec2 end ) {
|
||||||
|
draw_rect_tex((texture_t){0}, rgba, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui
|
||||||
|
|
||||||
|
static __thread array(guiskin_t) skins=0;
|
||||||
|
static __thread guiskin_t *last_skin=0;
|
||||||
|
static __thread map(int, gui_state_t) ctl_states=0; //@leak
|
||||||
|
|
||||||
|
void gui_pushskin(guiskin_t skin) {
|
||||||
|
array_push(skins, skin);
|
||||||
|
last_skin = array_back(skins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_popskin() {
|
||||||
|
if (!last_skin) return;
|
||||||
|
if (last_skin->free) last_skin->free(last_skin->userdata);
|
||||||
|
array_pop(skins);
|
||||||
|
last_skin = array_count(skins) ? array_back(skins) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *gui_userdata() {
|
||||||
|
return last_skin->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
gui_state_t *gui_getstate(int id, int kind) {
|
||||||
|
if (!ctl_states) map_init(ctl_states, less_int, hash_int);
|
||||||
|
static gui_state_t st={0};
|
||||||
|
st.kind=kind;
|
||||||
|
return map_find_or_add(ctl_states, id, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool (gui_button)(int id, vec4 r, const char *skin) {
|
||||||
|
gui_state_t *entry = gui_getstate(id, GUI_BUTTON);
|
||||||
|
bool was_clicked=0;
|
||||||
|
entry->hover = false;
|
||||||
|
|
||||||
|
if (input(MOUSE_X) > r.x && input(MOUSE_X) < r.z && input(MOUSE_Y) > r.y && input(MOUSE_Y) < r.w) {
|
||||||
|
if (input_up(MOUSE_L) && entry->held) {
|
||||||
|
was_clicked=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->held = input_held(MOUSE_L);
|
||||||
|
entry->hover = true;
|
||||||
|
}
|
||||||
|
else if (input_up(MOUSE_L) && entry->held) {
|
||||||
|
entry->held = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, *entry, skin, r);
|
||||||
|
else {
|
||||||
|
draw_rect(entry->held ? 0x111111FF : entry->hover ? 0xEEEEEEFF : 0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return was_clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (gui_panel)(int id, vec4 r, const char *skin) {
|
||||||
|
gui_state_t *entry = gui_getstate(id, GUI_PANEL);
|
||||||
|
if (last_skin->draw_rect_func) last_skin->draw_rect_func(last_skin->userdata, *entry, skin?skin:"panel", r);
|
||||||
|
else {
|
||||||
|
draw_rect(0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skinned */
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_free(void* userdata) {
|
||||||
|
skinned_t *a = C_CAST(skinned_t*, userdata);
|
||||||
|
atlas_destroy(&a->atlas);
|
||||||
|
FREE(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
atlas_slice_frame_t *skinned_getsliceframe(atlas_t *a, const char *name, const char *fallback) {
|
||||||
|
#define atlas_loop(n)\
|
||||||
|
for (int i = 0; i < array_count(a->slices); i++)\
|
||||||
|
if (!strcmp(quark_string(&a->db, a->slices[i].name), n))\
|
||||||
|
return &a->slice_frames[a->slices[i].frames[0]];
|
||||||
|
atlas_loop(name);
|
||||||
|
atlas_loop(fallback);
|
||||||
|
return NULL;
|
||||||
|
#undef atlas_loop
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_missing_rect(vec4 r) {
|
||||||
|
draw_rect_tex(texture_checker(), 0xFFFFFFFF, v42v2(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_sprite(float scale, atlas_t *a, atlas_slice_frame_t *f, vec4 r) {
|
||||||
|
if (!f->has_9slice) {
|
||||||
|
draw_rect_sheet(a->tex, v42v2(f->bounds), 0xFFFFFFFF, v42v2(r));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 outer = f->bounds;
|
||||||
|
vec4 core = f->core;
|
||||||
|
core.x += outer.x;
|
||||||
|
core.y += outer.y;
|
||||||
|
core.z += outer.x;
|
||||||
|
core.w += outer.y;
|
||||||
|
|
||||||
|
// Define the 9 slices
|
||||||
|
vec4 top_left_slice = {outer.x, outer.y, core.x, core.y};
|
||||||
|
vec4 top_middle_slice = {core.x, outer.y, core.z, core.y};
|
||||||
|
vec4 top_right_slice = {core.z, outer.y, outer.z, core.y};
|
||||||
|
|
||||||
|
vec4 middle_left_slice = {outer.x, core.y, core.x, core.w};
|
||||||
|
vec4 center_slice = core;
|
||||||
|
vec4 middle_right_slice = {core.z, core.y, outer.z, core.w};
|
||||||
|
|
||||||
|
vec4 bottom_left_slice = {outer.x, core.w, core.x, outer.w};
|
||||||
|
vec4 bottom_middle_slice = {core.x, core.w, core.z, outer.w};
|
||||||
|
vec4 bottom_right_slice = {core.z, core.w, outer.z, outer.w};
|
||||||
|
|
||||||
|
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};
|
||||||
|
vec4 bottom_right = {r.z - (outer.z - core.z) * scale, r.w - (outer.w - core.w) * scale, r.z, r.w};
|
||||||
|
|
||||||
|
vec4 top = {top_left.z, r.y, top_right.x, top_left.w};
|
||||||
|
vec4 bottom = {bottom_left.z, bottom_left.y, bottom_right.x, r.w};
|
||||||
|
vec4 left = {r.x, top_left.w, top_left.z, bottom_left.y};
|
||||||
|
vec4 right = {top_right.x, top_right.w, r.z, bottom_right.y};
|
||||||
|
|
||||||
|
vec4 center = {top_left.z, top_left.w, top_right.x, bottom_right.y};
|
||||||
|
|
||||||
|
draw_rect_sheet(a->tex, v42v2(center_slice), 0xFFFFFFFF, v42v2(center));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_left_slice), 0xFFFFFFFF, v42v2(top_left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_right_slice), 0xFFFFFFFF, v42v2(top_right));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_left_slice), 0xFFFFFFFF, v42v2(bottom_left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_right_slice), 0xFFFFFFFF, v42v2(bottom_right));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(top_middle_slice), 0xFFFFFFFF, v42v2(top));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(bottom_middle_slice), 0xFFFFFFFF, v42v2(bottom));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(middle_left_slice), 0xFFFFFFFF, v42v2(left));
|
||||||
|
draw_rect_sheet(a->tex, v42v2(middle_right_slice), 0xFFFFFFFF, v42v2(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_draw_rect(void* userdata, gui_state_t state, const char *skin, vec4 r) {
|
||||||
|
skinned_t *a = C_CAST(skinned_t*, userdata);
|
||||||
|
|
||||||
|
switch (state.kind) {
|
||||||
|
case GUI_BUTTON: {
|
||||||
|
char *btn = va("%s%s", skin?skin:a->button, state.held?"_press":state.hover?"_hover":"");
|
||||||
|
atlas_slice_frame_t *f = skinned_getsliceframe(&a->atlas, btn, skin?skin:a->button);
|
||||||
|
if (!f) skinned_draw_missing_rect(r);
|
||||||
|
else skinned_draw_sprite(a->scale, &a->atlas, f, r);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void skinned_preset_skins(skinned_t *s) {
|
||||||
|
s->panel = "panel";
|
||||||
|
s->button = "button";
|
||||||
|
}
|
||||||
|
|
||||||
|
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.free = skinned_free;
|
||||||
|
skinned_preset_skins(a);
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_input.c"
|
#line 1 "v4k_input.c"
|
||||||
// input framework
|
// input framework
|
||||||
// - rlyeh, public domain
|
// - rlyeh, public domain
|
||||||
|
@ -29056,22 +29366,31 @@ void editor_pump() {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
API void editor_cursorpos(int x, int y);
|
void editor_setmouse(int x, int y) {
|
||||||
void editor_cursorpos(int x, int y) {
|
|
||||||
glfwSetCursorPos( window_handle(), x, y );
|
glfwSetCursorPos( window_handle(), x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_symbol(int x, int y, const char *sym) {
|
vec2 editor_glyph(int x, int y, unsigned cp) {
|
||||||
// style: atlas size, unicode ranges and 6 font faces max
|
// 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_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}},
|
||||||
// style: 10 colors max
|
// style: 10 colors max
|
||||||
do_once font_color(FONT_COLOR1, WHITE);
|
do_once font_color(FONT_COLOR1, WHITE);
|
||||||
do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY);
|
do_once font_color(FONT_COLOR2, RGBX(0xE8F1FF,128)); // GRAY);
|
||||||
do_once font_color(FONT_COLOR3, YELLOW);
|
do_once font_color(FONT_COLOR3, YELLOW);
|
||||||
do_once font_color(FONT_COLOR4, ORANGE);
|
do_once font_color(FONT_COLOR4, ORANGE);
|
||||||
do_once font_color(FONT_COLOR5, CYAN);
|
do_once font_color(FONT_COLOR5, CYAN);
|
||||||
|
const char *sym = codepoint_to_utf8(cp);
|
||||||
font_goto(x,y);
|
font_goto(x,y);
|
||||||
font_print(va(FONT_FACE2 /*FONT_WHITE*/ FONT_H1 "%s", sym));
|
return font_print(va("%s" FONT_H1 "%s", cp >= ICON_MDI_MIN ? FONT_FACE3 : FONT_FACE2, sym));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 editor_glyphstr(int x, int y, const char *utf8) {
|
||||||
|
vec2 dim = {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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_frame( void (*game)(unsigned, float, double) ) {
|
void editor_frame( void (*game)(unsigned, float, double) ) {
|
||||||
|
|
57
engine/v4k.h
57
engine/v4k.h
|
@ -3984,6 +3984,8 @@ API char* strjoin(array(char*) list, const char *separator);
|
||||||
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
|
||||||
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
||||||
|
|
||||||
|
API const char* codepoint_to_utf8(unsigned cp);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ## string interning (quarks)
|
// ## string interning (quarks)
|
||||||
// - rlyeh, public domain.
|
// - rlyeh, public domain.
|
||||||
|
@ -4175,6 +4177,57 @@ API void sprite_edit(sprite_t *s);
|
||||||
API sprite_t*sprite_new(const char *ase, int bindings[6]);
|
API sprite_t*sprite_new(const char *ase, int bindings[6]);
|
||||||
API void sprite_del(sprite_t *s);
|
API void sprite_del(sprite_t *s);
|
||||||
API void sprite_setanim(sprite_t *s, unsigned name);
|
API void sprite_setanim(sprite_t *s, unsigned name);
|
||||||
|
#line 0
|
||||||
|
#line 1 "v4k_gui.h"
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// game ui
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GUI_PANEL,
|
||||||
|
GUI_BUTTON,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct gui_state_t {
|
||||||
|
int kind;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool held;
|
||||||
|
bool hover;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} gui_state_t;
|
||||||
|
|
||||||
|
typedef struct guiskin_t {
|
||||||
|
void (*draw_rect_func)(void* userdata, gui_state_t state, const char *skin, vec4 rect);
|
||||||
|
void (*free)(void* userdata);
|
||||||
|
void *userdata;
|
||||||
|
} guiskin_t;
|
||||||
|
|
||||||
|
API void gui_pushskin(guiskin_t skin);
|
||||||
|
API void* gui_userdata();
|
||||||
|
// --
|
||||||
|
API void gui_panel(int id, vec4 rect, const char *skin);
|
||||||
|
API bool gui_button(int id, vec4 rect, const char *skin);
|
||||||
|
API void gui_popskin();
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
#define gui_panel(...) gui_panel(__LINE__, __VA_ARGS__)
|
||||||
|
#define gui_button(...) gui_button(__LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
|
// default skins
|
||||||
|
API guiskin_t gui_skinned(const char *inifile, float scale);
|
||||||
|
|
||||||
|
typedef struct skinned_t {
|
||||||
|
atlas_t atlas;
|
||||||
|
float scale;
|
||||||
|
// unsigned framenum;
|
||||||
|
|
||||||
|
//skins
|
||||||
|
char *panel;
|
||||||
|
char *button;
|
||||||
|
} skinned_t;
|
||||||
|
|
||||||
#line 0
|
#line 0
|
||||||
|
|
||||||
#line 1 "v4k_system.h"
|
#line 1 "v4k_system.h"
|
||||||
|
@ -4757,7 +4810,9 @@ API void editor_inspect(obj *o);
|
||||||
API vec3 editor_pick(float mouse_x, float mouse_y);
|
API vec3 editor_pick(float mouse_x, float mouse_y);
|
||||||
API char* editor_path(const char *path);
|
API char* editor_path(const char *path);
|
||||||
|
|
||||||
API void editor_symbol(int x, int y, const char *sym);
|
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 void editor_gizmos(int dim);
|
API void editor_gizmos(int dim);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue