#include <alias/ui.h>
#include "client.h"
float scr_con_current; float scr_conlines;
bool scr_initialized;
int scr_draw_loading;
cvar_t *scr_conspeed;
cvar_t *scr_centertime;
cvar_t *scr_showturtle;
cvar_t *scr_showpause;
cvar_t *scr_printspeed;
cvar_t *scr_netgraph;
cvar_t *scr_timegraph;
cvar_t *scr_debuggraph;
cvar_t *scr_graphheight;
cvar_t *scr_graphscale;
cvar_t *scr_graphshift;
char crosshair_pic[MAX_QPATH];
int crosshair_width, crosshair_height;
void SCR_TimeRefresh_f(void);
void SCR_Loading_f(void);
void CL_AddNetgraph(void) {
int i;
int in;
int ping;
if(scr_debuggraph->value || scr_timegraph->value)
return;
for(i = 0; i < cls.netchan.dropped; i++)
SCR_DebugGraph(30, 0x40);
for(i = 0; i < cl.surpressCount; i++)
SCR_DebugGraph(30, 0xdf);
in = cls.netchan.incoming_acknowledged & (CMD_BACKUP - 1);
ping = cls.realtime - cl.cmd_time[in];
ping /= 30;
if(ping > 30)
ping = 30;
SCR_DebugGraph(ping, 0xd0);
}
typedef struct {
float value;
int color;
} graphsamp_t;
static int current;
static graphsamp_t values[1024];
void SCR_DebugGraph(float value, int color) {
values[current & 1023].value = value;
values[current & 1023].color = color;
current++;
}
void SCR_DrawDebugGraph(void) {
int a, x, y, w, i, h;
float v;
int color;
w = viddef.width;
x = 0;
y = viddef.height;
re.DrawFill(x, y - scr_graphheight->value, w, scr_graphheight->value, 8);
for(a = 0; a < w; a++) {
i = (current - 1 - a + 1024) & 1023;
v = values[i].value;
color = values[i].color;
v = v * scr_graphscale->value + scr_graphshift->value;
if(v < 0)
v += scr_graphheight->value * (1 + (int)(-v / scr_graphheight->value));
h = (int)v % (int)scr_graphheight->value;
re.DrawFill(x + w - 1 - a, y - h, 1, h, color);
}
}
char scr_centerstring[1024];
float scr_centertime_start; float scr_centertime_off;
int scr_center_lines;
int scr_erase_center;
void SCR_CenterPrint(char *str) {
char *s;
char line[64];
int i, j, l;
strncpy(scr_centerstring, str, sizeof(scr_centerstring) - 1);
scr_centertime_off = scr_centertime->value;
scr_centertime_start = cl.time;
scr_center_lines = 1;
s = str;
while(*s) {
if(*s == '\n')
scr_center_lines++;
s++;
}
Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\37\n\n");
s = str;
do {
for(l = 0; l < 40; l++)
if(s[l] == '\n' || !s[l])
break;
for(i = 0; i < (40 - l) / 2; i++)
line[i] = ' ';
for(j = 0; j < l; j++) {
line[i++] = s[j];
}
line[i] = '\n';
line[i + 1] = 0;
Com_Printf("%s", line);
while(*s && *s != '\n')
s++;
if(!*s)
break;
s++; } while(1);
Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\37\n\n");
Con_ClearNotify();
}
void SCR_DrawCenterString(void) {
char *start;
int l;
int j;
int x, y;
int remaining;
remaining = 9999;
scr_erase_center = 0;
start = scr_centerstring;
if(scr_center_lines <= 4)
y = viddef.height * 0.35;
else
y = 48;
do {
for(l = 0; l < 40; l++)
if(start[l] == '\n' || !start[l])
break;
x = (viddef.width - l * 8) / 2;
for(j = 0; j < l; j++, x += 8) {
re.DrawChar(x, y, start[j]);
if(!remaining--)
return;
}
y += 8;
while(*start && *start != '\n')
start++;
if(!*start)
break;
start++; } while(1);
}
void SCR_CheckDrawCenterString(void) {
scr_centertime_off -= cls.frametime;
if(scr_centertime_off <= 0)
return;
SCR_DrawCenterString();
}
void SCR_Sky_f(void) {
float rotate;
vec3_t axis;
if(Cmd_Argc() < 2) {
Com_Printf("Usage: sky <basename> <rotate> <axis x y z>\n");
return;
}
if(Cmd_Argc() > 2)
rotate = atof(Cmd_Argv(2));
else
rotate = 0;
if(Cmd_Argc() == 6) {
axis[0] = atof(Cmd_Argv(3));
axis[1] = atof(Cmd_Argv(4));
axis[2] = atof(Cmd_Argv(5));
} else {
axis[0] = 0;
axis[1] = 0;
axis[2] = 1;
}
re.SetSky(0, Cmd_Argv(1), rotate, axis);
}
void SCR_Init(void) {
scr_conspeed = Cvar_Get("scr_conspeed", "3", 0);
scr_showturtle = Cvar_Get("scr_showturtle", "0", 0);
scr_showpause = Cvar_Get("scr_showpause", "1", 0);
scr_centertime = Cvar_Get("scr_centertime", "2.5", 0);
scr_printspeed = Cvar_Get("scr_printspeed", "8", 0);
scr_netgraph = Cvar_Get("netgraph", "0", 0);
scr_timegraph = Cvar_Get("timegraph", "0", 0);
scr_debuggraph = Cvar_Get("debuggraph", "0", 0);
scr_graphheight = Cvar_Get("graphheight", "32", 0);
scr_graphscale = Cvar_Get("graphscale", "1", 0);
scr_graphshift = Cvar_Get("graphshift", "0", 0);
Cmd_AddCommand("timerefresh", SCR_TimeRefresh_f);
Cmd_AddCommand("loading", SCR_Loading_f);
Cmd_AddCommand("sky", SCR_Sky_f);
scr_initialized = true;
}
void SCR_DrawNet(void) {
if(cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < CMD_BACKUP - 1)
return;
re.DrawPic(64, 0, "net");
}
void SCR_DrawPause(void) {
if(!scr_showpause->value) return;
if(!cl_paused->value)
return;
UI_Align(0.5f, 0.5f);
UI_Picture("pause");
}
void SCR_DrawLoading(void) {
if(!scr_draw_loading)
return;
scr_draw_loading = false;
UI_Align(0.5f, 0.5f);
UI_Picture("loading");
}
void SCR_RunConsole(void) {
if(cls.key_dest == key_console)
scr_conlines = 0.5; else
scr_conlines = 0;
if(scr_conlines < scr_con_current) {
scr_con_current -= scr_conspeed->value * cls.frametime;
if(scr_conlines > scr_con_current)
scr_con_current = scr_conlines;
} else if(scr_conlines > scr_con_current) {
scr_con_current += scr_conspeed->value * cls.frametime;
if(scr_conlines < scr_con_current)
scr_con_current = scr_conlines;
}
}
void SCR_DrawConsole(void) {
Con_CheckResize();
if(cls.state == ca_disconnected || cls.state == ca_connecting) { Con_DrawConsole(1.0);
return;
}
if(cls.state != ca_active || !cl.refresh_prepped) { Con_DrawConsole(0.5);
re.DrawFill(0, viddef.height / 2, viddef.width, viddef.height / 2, 0);
return;
}
if(scr_con_current) {
Con_DrawConsole(scr_con_current);
} else {
if(cls.key_dest == key_game || cls.key_dest == key_message)
Con_DrawNotify(); }
}
void SCR_BeginLoadingPlaque(void) {
S_StopAllSounds();
cl.sound_prepped = false; if(cls.disable_screen)
return;
if(developer->value)
return;
if(cls.state == ca_disconnected)
return; if(cls.key_dest == key_console)
return;
if(cl.cinematictime > 0)
scr_draw_loading = 2; else
scr_draw_loading = 1;
SCR_UpdateScreen();
cls.disable_screen = Sys_Milliseconds();
cls.disable_servercount = cl.servercount;
}
void SCR_EndLoadingPlaque(void) {
cls.disable_screen = 0;
Con_ClearNotify();
}
void SCR_Loading_f(void) { SCR_BeginLoadingPlaque(); }
int entitycmpfnc(const entity_t *a, const entity_t *b) {
if(a->model == b->model) {
return ((intptr_t)a->skin - (intptr_t)b->skin);
} else {
return ((intptr_t)a->model - (intptr_t)b->model);
}
}
void SCR_TimeRefresh_f(void) {
int i;
int start, stop;
float time;
if(cls.state != ca_active)
return;
start = Sys_Milliseconds();
if(Cmd_Argc() == 2) { re.BeginFrame(0);
for(i = 0; i < 128; i++) {
cl.refdef.viewangles[1] = i / 128.0 * 360.0;
re.RenderFrame(&cl.refdef);
}
re.EndFrame();
} else {
for(i = 0; i < 128; i++) {
cl.refdef.viewangles[1] = i / 128.0 * 360.0;
re.BeginFrame(0);
re.RenderFrame(&cl.refdef);
re.EndFrame();
}
}
stop = Sys_Milliseconds();
time = (stop - start) / 1000.0;
Com_Printf("%f seconds (%f fps)\n", time, 128 / time);
}
#define STAT_MINUS 10
char *sb_nums[2][11] = {
{"num_0", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_minus"},
{"anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5", "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"}};
#define ICON_WIDTH 24
#define ICON_HEIGHT 24
#define CHAR_WIDTH 16
#define ICON_SPACE 8
void SizeHUDString(char *string, int *w, int *h) {
int lines, width, current;
lines = 1;
width = 0;
current = 0;
while(*string) {
if(*string == '\n') {
lines++;
current = 0;
} else {
current++;
if(current > width)
width = current;
}
string++;
}
*w = width * 8;
*h = lines * 8;
}
void DrawHUDString(char *string, int x, int y, int centerwidth, int xor) {
int margin;
char line[1024];
int width;
int i;
margin = x;
while(*string) {
width = 0;
while(*string && *string != '\n')
line[width++] = *string++;
line[width] = 0;
if(centerwidth)
x = margin + (centerwidth - width * 8) / 2;
else
x = margin;
for(i = 0; i < width; i++) {
re.DrawChar(x, y, line[i] ^ xor);
x += 8;
}
if(*string) {
string++; x = margin;
y += 8;
}
}
}
void SCR_DrawField(int x, int y, int color, int width, int value) {
char num[16], *ptr;
int l;
int frame;
if(width < 1)
return;
if(width > 5)
width = 5;
Com_sprintf(num, sizeof(num), "%i", value);
l = strlen(num);
if(l > width)
l = width;
x += 2 + CHAR_WIDTH * (width - l);
ptr = num;
while(*ptr && l) {
if(*ptr == '-')
frame = STAT_MINUS;
else
frame = *ptr - '0';
re.DrawPic(x, y, sb_nums[color][frame]);
x += CHAR_WIDTH;
ptr++;
l--;
}
}
void SCR_TouchPics(void) {
int i, j;
for(i = 0; i < 2; i++)
for(j = 0; j < 11; j++)
re.RegisterPic(sb_nums[i][j]);
if(crosshair->value) {
if(crosshair->value > 3 || crosshair->value < 0)
crosshair->value = 3;
Com_sprintf(crosshair_pic, sizeof(crosshair_pic), "ch%i", (int)(crosshair->value));
re.DrawGetPicSize(&crosshair_width, &crosshair_height, crosshair_pic);
if(!crosshair_width)
crosshair_pic[0] = 0;
}
}
void SCR_ExecuteLayoutString(const char *s) {
int x, y;
int value;
char *token;
int width;
int index;
clientinfo_t *ci;
if(cls.state != ca_active || !cl.refresh_prepped)
return;
if(!s[0])
return;
x = 0;
y = 0;
width = 3;
while(s) {
token = COM_Parse(&s);
if(!strcmp(token, "xl")) {
token = COM_Parse(&s);
x = atoi(token);
continue;
}
if(!strcmp(token, "xr")) {
token = COM_Parse(&s);
x = viddef.width + atoi(token);
continue;
}
if(!strcmp(token, "xv")) {
token = COM_Parse(&s);
x = viddef.width / 2 - 160 + atoi(token);
continue;
}
if(!strcmp(token, "yt")) {
token = COM_Parse(&s);
y = atoi(token);
continue;
}
if(!strcmp(token, "yb")) {
token = COM_Parse(&s);
y = viddef.height + atoi(token);
continue;
}
if(!strcmp(token, "yv")) {
token = COM_Parse(&s);
y = viddef.height / 2 - 120 + atoi(token);
continue;
}
if(!strcmp(token, "pic")) { token = COM_Parse(&s);
value = cl.frame.playerstate.stats[atoi(token)];
if(value >= MAX_IMAGES)
Com_Error(ERR_DROP, "Pic >= MAX_IMAGES");
if(cl.configstrings[CS_IMAGES + value][0] != 0) {
re.DrawPic(x, y, cl.configstrings[CS_IMAGES + value]);
}
continue;
}
if(!strcmp(token, "client")) { int score, ping, time;
token = COM_Parse(&s);
x = viddef.width / 2 - 160 + atoi(token);
token = COM_Parse(&s);
y = viddef.height / 2 - 120 + atoi(token);
token = COM_Parse(&s);
value = atoi(token);
if(value >= MAX_CLIENTS || value < 0)
Com_Error(ERR_DROP, "client >= MAX_CLIENTS");
ci = &cl.clientinfo[value];
token = COM_Parse(&s);
score = atoi(token);
token = COM_Parse(&s);
ping = atoi(token);
token = COM_Parse(&s);
time = atoi(token);
DrawAltString(x + 32, y, ci->name);
DrawString(x + 32, y + 8, "Score: ");
DrawAltString(x + 32 + 7 * 8, y + 8, va("%i", score));
DrawString(x + 32, y + 16, va("Ping: %i", ping));
DrawString(x + 32, y + 24, va("Time: %i", time));
if(!ci->icon)
ci = &cl.baseclientinfo;
re.DrawPic(x, y, ci->iconname);
continue;
}
if(!strcmp(token, "ctf")) { int score, ping;
char block[80];
token = COM_Parse(&s);
x = viddef.width / 2 - 160 + atoi(token);
token = COM_Parse(&s);
y = viddef.height / 2 - 120 + atoi(token);
token = COM_Parse(&s);
value = atoi(token);
if(value >= MAX_CLIENTS || value < 0)
Com_Error(ERR_DROP, "client >= MAX_CLIENTS");
ci = &cl.clientinfo[value];
token = COM_Parse(&s);
score = atoi(token);
token = COM_Parse(&s);
ping = atoi(token);
if(ping > 999)
ping = 999;
sprintf(block, sizeof(block), "%3d %3d %-12.12s", score, ping, ci->name);
if(value == cl.playernum)
DrawAltString(x, y, block);
else
DrawString(x, y, block);
continue;
}
if(!strcmp(token, "picn")) { token = COM_Parse(&s);
re.DrawPic(x, y, token);
continue;
}
if(!strcmp(token, "num")) { token = COM_Parse(&s);
width = atoi(token);
token = COM_Parse(&s);
value = cl.frame.playerstate.stats[atoi(token)];
SCR_DrawField(x, y, 0, width, value);
continue;
}
if(!strcmp(token, "hnum")) { int color;
width = 3;
value = cl.frame.playerstate.stats[STAT_HEALTH];
if(value > 25)
color = 0; else if(value > 0)
color = (cl.frame.serverframe >> 2) & 1; else
color = 1;
if(cl.frame.playerstate.stats[STAT_FLASHES] & 1)
re.DrawPic(x, y, "field_3");
SCR_DrawField(x, y, color, width, value);
continue;
}
if(!strcmp(token, "anum")) { int color;
width = 3;
value = cl.frame.playerstate.stats[STAT_AMMO];
if(value > 5)
color = 0; else if(value >= 0)
color = (cl.frame.serverframe >> 2) & 1; else
continue;
if(cl.frame.playerstate.stats[STAT_FLASHES] & 4)
re.DrawPic(x, y, "field_3");
SCR_DrawField(x, y, color, width, value);
continue;
}
if(!strcmp(token, "rnum")) { int color;
width = 3;
value = cl.frame.playerstate.stats[STAT_ARMOR];
if(value < 1)
continue;
color = 0;
if(cl.frame.playerstate.stats[STAT_FLASHES] & 2)
re.DrawPic(x, y, "field_3");
SCR_DrawField(x, y, color, width, value);
continue;
}
if(!strcmp(token, "stat_string")) {
token = COM_Parse(&s);
index = atoi(token);
if(index < 0 || index >= MAX_CONFIGSTRINGS)
Com_Error(ERR_DROP, "Bad stat_string index");
index = cl.frame.playerstate.stats[index];
if(index < 0 || index >= MAX_CONFIGSTRINGS)
Com_Error(ERR_DROP, "Bad stat_string index");
DrawString(x, y, cl.configstrings[index]);
continue;
}
if(!strcmp(token, "cstring")) {
token = COM_Parse(&s);
DrawHUDString(token, x, y, 320, 0);
continue;
}
if(!strcmp(token, "string")) {
token = COM_Parse(&s);
DrawString(x, y, token);
continue;
}
if(!strcmp(token, "cstring2")) {
token = COM_Parse(&s);
DrawHUDString(token, x, y, 320, 0x80);
continue;
}
if(!strcmp(token, "string2")) {
token = COM_Parse(&s);
DrawAltString(x, y, token);
continue;
}
if(!strcmp(token, "if")) { token = COM_Parse(&s);
value = cl.frame.playerstate.stats[atoi(token)];
if(!value) { while(s && strcmp(token, "endif")) {
token = COM_Parse(&s);
}
}
continue;
}
}
}
void SCR_DrawStats(void) { SCR_ExecuteLayoutString(cl.configstrings[CS_STATUSBAR]); }
#define STAT_LAYOUTS 13
void SCR_DrawLayout(void) {
if(!cl.frame.playerstate.stats[STAT_LAYOUTS])
return;
SCR_ExecuteLayoutString(cl.layout);
}
static alias_ui_OutputGroup ui_output_groups[1024];
static uint32_t ui_output_indexes[1 << 10];
static struct DrawVertex ui_output_vertexes[1 << 10];
static struct BaseImage *ui_frame_images[1024];
static alias_ui *frame_ui = NULL;
uint32_t UI_GetTextureIndex(const char *name) {
uint32_t i;
for(i = 0; ui_frame_images[i]; i++) {
if(strcmp(name, ui_frame_images[i]->name) == 0) {
return i;
}
}
ui_frame_images[i] = re.RegisterPic(name);
ui_frame_images[i + 1] = NULL;
return i;
}
void UI_SetTexture(const char *name) { alias_ui_set_texture(frame_ui, UI_GetTextureIndex(name)); }
void UI_Align(float align_x, float align_y) { alias_ui_align(frame_ui, align_x, align_y); }
void UI_Horizontal(void) { alias_ui_begin_horizontal_stack(frame_ui); }
void UI_Vertical(void) { alias_ui_begin_vertical_stack(frame_ui); }
void UI_End(void) { alias_ui_end_stack(frame_ui); }
void UI_ForceSize(float w, float h) { alias_ui_size(frame_ui, w, h); }
void UI_ForceWidth(float w) { alias_ui_width(frame_ui, w); }
void UI_ForceHeight(float h) { alias_ui_height(frame_ui, h); }
void UI_Picture(const char *name, ...) {
char path[MAX_QPATH];
va_list ap;
va_start(ap, name);
vsprintf(path, sizeof(path), name, ap);
va_end(ap);
int index = UI_GetTextureIndex(path);
const struct BaseImage *image = ui_frame_images[index];
if(image == NULL)
return;
alias_ui_image(frame_ui, image->width, image->height, image->s0, image->t0, image->s1, image->t1, index);
}
void UI_Fill(float r, float g, float b, float a) {
alias_ui_color_fill(frame_ui, r, g, b, a);
}
static void text_size(alias_ui *ui, const char *buffer, alias_R size, alias_R max_width, alias_R *out_width,
alias_R *out_height) {
*out_width = strlen(buffer) * 8;
*out_height = 8;
}
static void text_draw(alias_ui *ui, const char *buffer, alias_R x, alias_R y, alias_R width, alias_R size,
alias_Color color) {
UI_SetTexture("conchars");
for(int i = 0; buffer[i]; i++, x += 8) {
int num = buffer[i];
int row, col;
float frow, fcol, size;
num &= 255;
if((num & 127) == 32)
continue;
if(y <= -8)
continue;
row = num >> 4;
col = num & 15;
frow = row * 0.0625;
fcol = col * 0.0625;
size = 0.0625;
alias_ui_draw_rectangle(ui, x, y, 8, 8, fcol, frow, fcol + size, frow + size, color.r, color.g, color.b, color.a);
}
}
void UI_Text(const char *format, ...) {
va_list ap;
va_start(ap, format);
alias_ui_textv(frame_ui, format, ap);
va_end(ap);
}
void SCR_UpdateScreen(void) {
void SNDDMA_DrawStats(void);
if(cls.disable_screen) {
if(Sys_Milliseconds() - cls.disable_screen > 120000) {
cls.disable_screen = 0;
Com_Printf("Loading plaque timed out.\n");
}
return;
}
if(!scr_initialized || !con.initialized)
return;
if(frame_ui == NULL) {
alias_ui_initialize(alias_default_MemoryCB(), &frame_ui);
}
alias_ui_Input ui_input = {
.screen_size = {.width = viddef.width, .height = viddef.height}, .text_draw = text_draw, .text_size = text_size};
ui_frame_images[0] = NULL;
UI_GetTextureIndex("white");
alias_ui_begin_frame(frame_ui, alias_default_MemoryCB(), &ui_input);
alias_ui_begin_stack(frame_ui);
re.BeginFrame(0.0f);
if(scr_draw_loading == 2) { re.CinematicSetPalette(NULL);
scr_draw_loading = false;
UI_Align(0.5f, 0.5f);
UI_Picture("loading");
}
else if(cl.cinematictime > 0) {
if(cls.key_dest == key_menu) {
if(cl.cinematicpalette_active) {
re.CinematicSetPalette(NULL);
cl.cinematicpalette_active = false;
}
M_Draw();
} else if(cls.key_dest == key_console) {
if(cl.cinematicpalette_active) {
re.CinematicSetPalette(NULL);
cl.cinematicpalette_active = false;
}
SCR_DrawConsole();
} else {
SCR_DrawCinematic();
}
} else {
if(cl.cinematicpalette_active) {
re.CinematicSetPalette(NULL);
cl.cinematicpalette_active = false;
}
V_RenderView(0.0f);
SCR_DrawStats();
if(cl.frame.playerstate.stats[STAT_LAYOUTS] & 1)
SCR_DrawLayout();
if(cl.frame.playerstate.stats[STAT_LAYOUTS] & 2)
CL_DrawInventory();
SCR_DrawNet();
SCR_CheckDrawCenterString();
if(scr_timegraph->value)
SCR_DebugGraph(cls.frametime * 300, 0);
if(scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value)
SCR_DrawDebugGraph();
SCR_DrawPause();
SCR_DrawConsole();
M_Draw();
SCR_DrawLoading();
UI_Vertical();
SNDDMA_DrawStats();
UI_End();
}
{
extern void alias_gl_temporaryBufferStats(uint32_t type, uint32_t * total_allocated, uint32_t * used);
UI_Align(0, 1);
UI_Vertical();
uint32_t element_alloc, element_used;
alias_gl_temporaryBufferStats(0x8893, &element_alloc, &element_used);
UI_Text("Temp Element Buffer: %i used of %i", element_used, element_alloc);
uint32_t vertex_alloc, vertex_used;
alias_gl_temporaryBufferStats(0x8892, &vertex_alloc, &vertex_used);
UI_Text("Temp Vertex Buffer: %i used of %i", vertex_used, vertex_alloc);
UI_End();
}
UI_Align(1, 1);
UI_Text("Elysian Break pre-alpha");
alias_ui_end_stack(frame_ui);
alias_ui_Output ui_output = {
.groups = ui_output_groups,
.num_groups = 0,
.max_groups = sizeof(ui_output_groups) / sizeof(ui_output_groups[0]),
.num_vertexes = 0,
.index_sub_buffer = {.pointer = ui_output_indexes,
.stride = sizeof(ui_output_indexes[0]),
.count = sizeof(ui_output_indexes) / sizeof(ui_output_indexes[0]),
.type_format = alias_memory_Format_Uint32,
.type_length = 1},
.num_vertexes = 0,
.xy_sub_buffer = {.pointer = &ui_output_vertexes[0].xy[0],
.stride = sizeof(ui_output_vertexes[0]),
.count = sizeof(ui_output_vertexes) / sizeof(ui_output_vertexes[0]),
.type_format = alias_memory_Format_Float32,
.type_length = 2},
.st_sub_buffer = {.pointer = &ui_output_vertexes[0].st[0],
.stride = sizeof(ui_output_vertexes[0]),
.count = sizeof(ui_output_vertexes) / sizeof(ui_output_vertexes[0]),
.type_format = alias_memory_Format_Float32,
.type_length = 2},
.rgba_sub_buffer = {.pointer = &ui_output_vertexes[0].rgba,
.stride = sizeof(ui_output_vertexes[0]),
.count = sizeof(ui_output_vertexes) / sizeof(ui_output_vertexes[0]),
.type_format = alias_memory_Format_Unorm8,
.type_length = 4},
};
alias_ui_end_frame(frame_ui, alias_default_MemoryCB(), &ui_output);
for(int i = 0; i < ui_output.num_groups; i++) {
re.DrawTriangles(ui_frame_images[ui_output_groups[i].texture_id], ui_output_vertexes, ui_output.num_vertexes,
ui_output_indexes + ui_output_groups[i].index, ui_output_groups[i].length);
}
re.EndFrame();
}