get ui up and running, weird initial frame size issue

main
Patrick Cleavelin 2024-05-27 15:56:30 -05:00
parent 44d28ae5fd
commit 5822c54bf3
7 changed files with 218 additions and 52 deletions

View File

@ -42,7 +42,7 @@ run: build
[windows] [windows]
build_gfx: compile_shaders_directx build_gfx: compile_shaders_directx
if not exist bin\ mkdir bin if not exist bin\ mkdir bin
cl /nologo -Zi /c /Fdbin\ /Fobin\ /Ivendor\ /DWIN32_LEAN_AND_MEAN /D_FONT_WIDTH=12 /D_FONT_HEIGHT=24 /DED_GFX_IMPLEMENTATION /std:c11 /TC vendor\pcleavelin\gfx.h cl /nologo -Zi /c /Fdbin\ /Fobin\ /Ivendor\ /DWIN32_LEAN_AND_MEAN /D_FONT_WIDTH=16 /D_FONT_HEIGHT=32 /DED_GFX_IMPLEMENTATION /std:c11 /TC vendor\pcleavelin\gfx.h
[windows] [windows]
compile_shaders_directx: compile_shaders_directx:

View File

@ -2,6 +2,7 @@
struct VertexOutput { struct VertexOutput {
vec4 position; vec4 position;
vec4 color;
vec2 tex_coord; vec2 tex_coord;
}; };
@ -14,5 +15,5 @@ out vec4 color;
void main() { void main() {
float text_color = texture(atlas_texture, out_vertex.tex_coord).r; float text_color = texture(atlas_texture, out_vertex.tex_coord).r;
color = vec4(text_color * vec3(1,1,1), text_color); color = vec4(out_vertex.color.rgb * text_color, text_color);
} }

View File

@ -7,6 +7,7 @@ struct Vertex {
struct VertexOutput { struct VertexOutput {
vec4 position; vec4 position;
vec4 color;
vec2 tex_coord; vec2 tex_coord;
}; };
@ -16,6 +17,7 @@ struct Glyph {
vec2 target_position; vec2 target_position;
float y_offset; float y_offset;
float _haha_alignment; float _haha_alignment;
vec4 color;
}; };
layout(std430, binding = 0) readonly buffer VertexBlock { layout(std430, binding = 0) readonly buffer VertexBlock {
@ -50,6 +52,7 @@ void main() {
device_position.y = -device_position.y; device_position.y = -device_position.y;
out_vertex.position = device_position; out_vertex.position = device_position;
out_vertex.color = glyph.color;
out_vertex.tex_coord = atlas_position; out_vertex.tex_coord = atlas_position;
gl_Position = device_position; gl_Position = device_position;

View File

@ -1,6 +1,7 @@
package gfx; package gfx;
import "core:strings" import "core:strings"
import "core:fmt"
import c "core:c" import c "core:c"
when ODIN_OS_STRING == "unix" { when ODIN_OS_STRING == "unix" {
@ -41,6 +42,8 @@ foreign gfx {
@(link_name="gfx_frame_width") frame_width :: proc "c" (gfx_cx: ^cx) -> u32 --- @(link_name="gfx_frame_width") frame_width :: proc "c" (gfx_cx: ^cx) -> u32 ---
@(link_name="gfx_frame_height") frame_height :: proc "c" (gfx_cx: ^cx) -> u32 --- @(link_name="gfx_frame_height") frame_height :: proc "c" (gfx_cx: ^cx) -> u32 ---
@(link_name="gfx_mouse_x") mouse_x :: proc "c" (gfx_cx: ^cx) -> i32 ---
@(link_name="gfx_mouse_y") mouse_y :: proc "c" (gfx_cx: ^cx) -> i32 ---
@(link_name="gfx_init_context") init_context :: proc "c" (frame_func: GfxFrameFunc, width: u32, height: u32) -> ^cx --- @(link_name="gfx_init_context") init_context :: proc "c" (frame_func: GfxFrameFunc, width: u32, height: u32) -> ^cx ---
@(link_name="gfx_run_events") run_events :: proc "c" (gfx_cx: ^cx) --- @(link_name="gfx_run_events") run_events :: proc "c" (gfx_cx: ^cx) ---
@ -54,15 +57,18 @@ foreign gfx {
} }
queue_text :: proc(gfx_cx: ^cx, text: string, position: [2]f32, max_x: f32, max_y: f32, color: [4]f32) { queue_text :: proc(gfx_cx: ^cx, text: string, position: [2]f32, max_x: f32, max_y: f32, color: [4]f32) {
ctext_data := strings.clone_to_cstring(text)
text_ := gfx_string { text_ := gfx_string {
data = raw_data(text), data = transmute([^]u8)ctext_data,
len = len(text), len = len(ctext_data),
}; };
p := []f32 { position[0], position[1] } p := []f32 { position[0], position[1] }
c := []f32 { color[0], color[1], color[2], color[3] } c := []f32 { color[0], color[1], color[2], color[3] }
gfx_queue_text(gfx_cx, text_, raw_data(p), max_x, max_y, raw_data(c)) gfx_queue_text(gfx_cx, text_, raw_data(p), max_x, max_y, raw_data(c))
delete(ctext_data)
} }
queue_ui_rect :: proc(gfx_cx: ^cx, position: [2]f32, size: [2]f32, border_size: f32, color: [4]f32) { queue_ui_rect :: proc(gfx_cx: ^cx, position: [2]f32, size: [2]f32, border_size: f32, color: [4]f32) {

View File

@ -1,81 +1,160 @@
package main package main
import "core:runtime"; import "core:runtime"
import "core:fmt"; import "core:fmt"
import "core:os"; import "core:os"
import stbtt "vendor:stb/truetype"; import stbtt "vendor:stb/truetype"
import ui "vendor:microui"
import "gfx"; import "gfx"
gfx_cx: ^gfx.cx = nil gfx_cx: ^gfx.cx = nil
ui_cx: ui.Context
last_mouse_left_down := false;
last_mouse_right_down := false;
rasterized_font_height: i32 = 16
frame_func :: proc "c" (mouse_x: int, mouse_y: int, mouse_left_down: bool, mouse_right_down: bool) { frame_func :: proc "c" (mouse_x: int, mouse_y: int, mouse_left_down: bool, mouse_right_down: bool) {
context = runtime.default_context(); context = runtime.default_context()
frame_width := gfx.frame_width(gfx_cx)
frame_height := gfx.frame_height(gfx_cx)
for y in 0..<5 { ui.input_mouse_move(&ui_cx, i32(mouse_x), i32(mouse_y));
for x in 0..<5 { if mouse_left_down && !last_mouse_left_down {
gfx.queue_text(gfx_cx, "1234", { f32(x)*100, f32(x)*24 + f32(y)*26}, 1000, 1000, { 1, 1, 1, 1 }); ui.input_mouse_down(&ui_cx, i32(mouse_x), i32(mouse_y), .LEFT);
} else if !mouse_left_down && last_mouse_left_down {
ui.input_mouse_up(&ui_cx, i32(mouse_x), i32(mouse_y), .LEFT);
}
last_mouse_left_down = mouse_left_down;
last_mouse_right_down = mouse_right_down;
ui.begin(&ui_cx)
{
defer ui.end(&ui_cx)
if ui.begin_window(&ui_cx, "canvas", ui.Rect{ 0, 0, i32(frame_width), i32(frame_height) }, opt = { .EXPANDED, .NO_CLOSE, .NO_RESIZE, .NO_INTERACT, .NO_TITLE }) {
defer ui.end_window(&ui_cx)
ui.layout_row(&ui_cx, []i32 { 180, -1 }, -1)
ui.begin_panel(&ui_cx, "top panel", opt = { .AUTO_SIZE })
{
defer ui.end_panel(&ui_cx)
ui.layout_row(&ui_cx, []i32 { -1 }, 24);
if ui.button(&ui_cx, "Mock Channel 1") >= { .SUBMIT } {
fmt.println("mock channel 1")
}
if ui.button(&ui_cx, "Mock Channel 2") >= { .SUBMIT } {
fmt.println("mock channel 2")
}
if ui.button(&ui_cx, "Mock Channel 3") >= { .SUBMIT } {
fmt.println("mock channel 3")
}
}
ui.layout_begin_column(&ui_cx);
{
defer ui.layout_end_column(&ui_cx);
ui.layout_row(&ui_cx, []i32 { -1 }, -64);
ui.begin_panel(&ui_cx, "channel view")
{
defer ui.end_panel(&ui_cx)
ui.text(&ui_cx, "TODO: put slack channel messages here")
}
ui.layout_row(&ui_cx, []i32 { -1 }, -1);
ui.begin_panel(&ui_cx, "text edit view")
{
defer ui.end_panel(&ui_cx)
ui.text(&ui_cx, "TODO: add text box here")
}
}
}
canvas := ui.get_container(&ui_cx, "canvas");
if canvas != nil {
canvas.rect.w = i32(frame_width);
canvas.rect.h = i32(frame_height);
} }
} }
frame_width := gfx.frame_width(gfx_cx); cmd: ^ui.Command = nil
frame_height := gfx.frame_height(gfx_cx); for {
variant, has_next := ui.next_command_iterator(&ui_cx, &cmd)
if !has_next {
break
}
gfx.queue_ui_rect(gfx_cx, { f32(frame_width/2-32), f32(frame_height/2-32) }, { 64, 64 }, 8, { 1, 0, 1, 1 }); switch v in variant {
case ^ui.Command_Text: {
gfx.queue_text(gfx_cx, v.str, { f32(v.pos.x), f32(v.pos.y-rasterized_font_height) }, 1000, 1000, { f32(v.color.r)/255.0, f32(v.color.g)/255.0, f32(v.color.r)/255.0, f32(v.color.a)/255.0 })
}
case ^ui.Command_Rect: {
gfx.queue_ui_rect(gfx_cx, { f32(v.rect.x), f32(v.rect.y) }, { f32(v.rect.w), f32(v.rect.h) }, 0, { f32(v.color.r)/255.0, f32(v.color.g)/255.0, f32(v.color.r)/255.0, f32(v.color.a)/255.0 })
}
case ^ui.Command_Icon: {}
case ^ui.Command_Clip: {}
case ^ui.Command_Jump: {}
}
}
} }
main :: proc() { main :: proc() {
gfx_cx = gfx.init_context(frame_func, 800, 600) gfx_cx = gfx.init_context(frame_func, 800, 600)
font_data, success := os.read_entire_file_from_filename("./bin/JetBrainsMono-Medium.ttf"); font_data, success := os.read_entire_file_from_filename("./bin/JetBrainsMono-Medium.ttf")
if !success { if !success {
fmt.eprintln("failed to read font file"); fmt.eprintln("failed to read font file")
os.exit(1); os.exit(1)
} }
font: stbtt.fontinfo; font: stbtt.fontinfo
if stbtt.InitFont(&font, raw_data(font_data), 0) == false { if stbtt.InitFont(&font, raw_data(font_data), 0) == false {
fmt.eprintln("failed to init font"); fmt.eprintln("failed to init font")
os.exit(1); os.exit(1)
} }
bitmap_size: i32 = 512; bitmap_size: i32 = 512
bitmap := make([]u8, bitmap_size * bitmap_size); bitmap := make([]u8, bitmap_size * bitmap_size)
rasterized_font_height: i32 = 24; ascent, descent, line_gap: i32
scale := stbtt.ScaleForPixelHeight(&font, f32(rasterized_font_height * 2))
ascent, descent, line_gap: i32; stbtt.GetFontVMetrics(&font, &ascent, &descent, &line_gap)
scale := stbtt.ScaleForPixelHeight(&font, f32(rasterized_font_height * 2));
stbtt.GetFontVMetrics(&font, &ascent, &descent, &line_gap);
gfx.add_glyph(gfx_cx, gfx.GpuGlyph { gfx.add_glyph(gfx_cx, gfx.GpuGlyph {
size = {f32(rasterized_font_height/4), 1}, size = {f32(rasterized_font_height/4), 1},
y_offset = f32(-rasterized_font_height), y_offset = f32(-rasterized_font_height),
}); })
x := rasterized_font_height / 4; x := rasterized_font_height / 4
y: i32 = 0; y: i32 = 0
for i in 33..<33+96 { for i in 33..<33+96 {
width, height, xoff, yoff: i32; width, height, xoff, yoff: i32
glyph_bitmap := stbtt.GetCodepointBitmap( glyph_bitmap := stbtt.GetCodepointBitmap(
&font, scale, scale, rune(i), &width, &height, &xoff, &yoff); &font, scale, scale, rune(i), &width, &height, &xoff, &yoff)
if (x + width) >= bitmap_size { if (x + width) >= bitmap_size {
x = 0; x = 0
y += i32(f32(ascent - descent + line_gap) * scale); y += i32(f32(ascent - descent + line_gap) * scale)
} }
xxx: i32 = x; xxx: i32 = x
for xx in 0..<width { for xx in 0..<width {
yyy: i32 = y; yyy: i32 = y
for yy in 0..<height { for yy in 0..<height {
bitmap[xxx + yyy * bitmap_size] = bitmap[xxx + yyy * bitmap_size] =
glyph_bitmap[xx + yy * width]; glyph_bitmap[xx + yy * width]
yyy += 1; yyy += 1
} }
xxx += 1; xxx += 1
} }
gfx.add_glyph(gfx_cx, gfx.add_glyph(gfx_cx,
@ -85,11 +164,48 @@ main :: proc() {
position = {f32(x), f32(y)}, position = {f32(x), f32(y)},
y_offset = f32(yoff), y_offset = f32(yoff),
} }
); )
x += width; x += width
} }
gfx.push_texture_buffer(gfx_cx, u32(bitmap_size), u32(bitmap_size), raw_data(bitmap), u32(bitmap_size * bitmap_size)); gfx.push_texture_buffer(gfx_cx, u32(bitmap_size), u32(bitmap_size), raw_data(bitmap), u32(bitmap_size * bitmap_size))
ui.init(&ui_cx)
ui_cx.text_width = proc(font: ui.Font, str: string) -> i32 {
return (rasterized_font_height/2) * i32(len(str)) + (rasterized_font_height/4) * 4
}
ui_cx.text_height = proc(font: ui.Font) -> i32 {
return rasterized_font_height
}
// ui_cx.style = &ui.Style {
// size = ui.Vec2 { 0, 0 },
// padding = 5,
// spacing = 4,
// indent = 24,
// title_height = rasterized_font_height,
// scrollbar_size = 12,
// thumb_size = 8,
// colors = [ui.Color_Type]ui.Color {
// .TEXT = ui.Color { 230, 230, 230, 255 },
// .BORDER = ui.Color { 25, 25, 25, 255 },
// .WINDOW_BG = ui.Color { 50, 50, 50, 255 },
// .TITLE_BG = ui.Color { 25, 25, 25, 255 },
// .TITLE_TEXT = ui.Color { 240, 240, 240, 255 },
// .PANEL_BG = ui.Color { 0, 0, 0, 0 },
// .BUTTON = ui.Color { 75, 75, 75, 255 },
// .BUTTON_HOVER = ui.Color { 95, 95, 95, 255 },
// .BUTTON_FOCUS = ui.Color { 115, 115, 115, 255 },
// .BASE = ui.Color { 30, 30, 30, 255 },
// .BASE_HOVER = ui.Color { 35, 35, 35, 255 },
// .BASE_FOCUS = ui.Color { 40, 40, 40, 255 },
// .SCROLL_BASE = ui.Color { 43, 43, 43, 255 },
// .SCROLL_THUMB = ui.Color { 30, 30, 30, 255 },
// },
// };
frame_func(0,0,false,false);
frame_func(0,0,false,false);
for(gfx.keep_running) { for(gfx.keep_running) {
gfx.run_events(gfx_cx) gfx.run_events(gfx_cx)

View File

@ -56,9 +56,13 @@ VOID CALLBACK FileIOCompletionRoutine(
printf("Number of bytes: %li", dwNumberOfBytesTransfered); printf("Number of bytes: %li", dwNumberOfBytesTransfered);
} }
uint64_t get_file_size(string filePath) { uint64_t get_file_size(string file_path) {
char *file_path_with_sentinel = malloc(file_path.len + 1);
memcpy(file_path_with_sentinel, file_path.data, file_path.len + 1);
file_path_with_sentinel[file_path.len] = 0;
// FIXME: convert to null teminated string // FIXME: convert to null teminated string
HANDLE hFile = CreateFile(filePath.data, HANDLE hFile = CreateFile(file_path_with_sentinel,
GENERIC_READ, GENERIC_READ,
FILE_SHARE_READ, FILE_SHARE_READ,
NULL, NULL,
@ -69,12 +73,17 @@ uint64_t get_file_size(string filePath) {
LARGE_INTEGER size; LARGE_INTEGER size;
assert(GetFileSizeEx(hFile, &size) && "get_file_size: failed to get file size"); assert(GetFileSizeEx(hFile, &size) && "get_file_size: failed to get file size");
free(file_path_with_sentinel);
return size.LowPart; return size.LowPart;
} }
bool load_file(string filePath, size_t size, void *buffer) { bool load_file(string file_path, size_t size, void *buffer) {
char *file_path_with_sentinel = malloc(file_path.len + 1);
memcpy(file_path_with_sentinel, file_path.data, file_path.len + 1);
file_path_with_sentinel[file_path.len] = 0;
// FIXME: convert to null teminated string // FIXME: convert to null teminated string
HANDLE hFile = CreateFile(filePath.data, HANDLE hFile = CreateFile(file_path_with_sentinel,
GENERIC_READ, GENERIC_READ,
FILE_SHARE_READ, FILE_SHARE_READ,
NULL, NULL,
@ -93,6 +102,7 @@ bool load_file(string filePath, size_t size, void *buffer) {
} }
assert(bytesRead == size && "load_file: didn't one-shot read the whole file"); assert(bytesRead == size && "load_file: didn't one-shot read the whole file");
free(file_path_with_sentinel);
return true; return true;
} }
#elif defined(__APPLE__) #elif defined(__APPLE__)

View File

@ -219,9 +219,11 @@ typedef struct {
} gfx_context_t; } gfx_context_t;
static gfx_context_t _gfx_context; static gfx_context_t _gfx_context;
#ifdef ED_GFX_IMPLEMENTATION void gfx_queue_text(gfx_context_t *cx, string text, float position[2],
float max_x, float max_y, float color[4]);
void gfx_update_buffer(gfx_context_t *cx, size_t buffer_index, const void *data, void gfx_update_buffer(gfx_context_t *cx, size_t buffer_index, const void *data,
size_t len); size_t len);
#ifdef ED_GFX_IMPLEMENTATION
#if defined(__APPLE__) #if defined(__APPLE__)
static void _metal_gfx_present(_metal_gfx_context *cx); static void _metal_gfx_present(_metal_gfx_context *cx);
@ -1307,6 +1309,25 @@ static LRESULT CALLBACK _win32_gfx_window_proc(HWND window, UINT message, WPARAM
_gfx_context.frame_height = height; _gfx_context.frame_height = height;
break; break;
} }
case WM_LBUTTONDOWN:
{
_gfx_context.backend.mouse_left_down = true;
break;
}
case WM_LBUTTONUP:
{
_gfx_context.backend.mouse_left_down = false;
break;
}
case WM_MOUSEMOVE:
{
UINT mouse_x = LOWORD(lparam);
UINT mouse_y = HIWORD(lparam);
_gfx_context.backend.mouse_x = mouse_x;
_gfx_context.backend.mouse_y = mouse_y;
break;
}
case WM_PAINT: case WM_PAINT:
{ {
_win32_gfx_present(&_gfx_context.backend); _win32_gfx_present(&_gfx_context.backend);
@ -1344,6 +1365,7 @@ _win32_gfx_init_context(uint32_t width, uint32_t height) {
WNDCLASS win_class = {0}; WNDCLASS win_class = {0};
win_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; win_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
win_class.hInstance = instance; win_class.hInstance = instance;
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.lpszClassName = "GFXWindowClass"; win_class.lpszClassName = "GFXWindowClass";
win_class.lpfnWndProc = _win32_gfx_window_proc; win_class.lpfnWndProc = _win32_gfx_window_proc;
assert(RegisterClass(&win_class) && "_win32_gfx_init_context: RegisterClass failed"); assert(RegisterClass(&win_class) && "_win32_gfx_init_context: RegisterClass failed");
@ -1375,7 +1397,7 @@ _win32_gfx_init_context(uint32_t width, uint32_t height) {
pixel_format_desc.iPixelType = PFD_TYPE_RGBA; pixel_format_desc.iPixelType = PFD_TYPE_RGBA;
pixel_format_desc.cColorBits = 32; pixel_format_desc.cColorBits = 32;
pixel_format_desc.cAlphaBits = 32; pixel_format_desc.cAlphaBits = 32;
pixel_format_desc.cDepthBits = 24; pixel_format_desc.cDepthBits = 0;
int pixel_format_id = ChoosePixelFormat(device_context, &pixel_format_desc); int pixel_format_id = ChoosePixelFormat(device_context, &pixel_format_desc);
assert(pixel_format_id && "_win32_gfx_init_context: ChoosePixelFormat failed"); assert(pixel_format_id && "_win32_gfx_init_context: ChoosePixelFormat failed");
@ -1472,12 +1494,12 @@ _win32_gfx_init_context(uint32_t width, uint32_t height) {
} }
static void _win32_gfx_send_events(_win32_gfx_context *cx) { static void _win32_gfx_send_events(_win32_gfx_context *cx) {
_win32_gfx_present(cx);
MSG message; MSG message;
GetMessage(&message, 0, 0 ,0); GetMessage(&message, 0, 0 ,0);
TranslateMessage(&message); TranslateMessage(&message);
DispatchMessage(&message); DispatchMessage(&message);
_win32_gfx_present(cx);
} }
static void _win32_gfx_present(_win32_gfx_context *cx) { static void _win32_gfx_present(_win32_gfx_context *cx) {
@ -1749,6 +1771,14 @@ uint32_t gfx_frame_height(gfx_context_t *cx) {
return cx->frame_height; return cx->frame_height;
} }
int32_t gfx_mouse_x(gfx_context_t *cx) {
return cx->backend.mouse_x;
}
int32_t gfx_mouse_y(gfx_context_t *cx) {
return cx->backend.mouse_y;
}
void *gfx_init_context(_gfx_frame_func frame_func, uint32_t width, void *gfx_init_context(_gfx_frame_func frame_func, uint32_t width,
uint32_t height) { uint32_t height) {
#if defined(__APPLE__) #if defined(__APPLE__)