Unicode used in multimedia

This commit is contained in:
Krzosa Karol
2022-05-18 11:54:27 +02:00
parent 59b842018f
commit 61c3dfb48d
8 changed files with 83 additions and 3398 deletions

View File

@@ -1,454 +0,0 @@
function U8
to_lower_case(U8 a) {
if (a >= 'A' && a <= 'Z')
a += 32;
return a;
}
function U8
to_upper_case(U8 a) {
if (a >= 'a' && a <= 'z')
a -= 32;
return a;
}
function U8
char_to_lower(U8 c){
if(c >= 'A' && c <= 'Z')
c += 32;
return c;
}
function U8
char_to_upper(U8 c){
if(c >= 'a' && c <= 'z')
c -= 32;
return c;
}
function B32
is_whitespace(U8 w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
function B32
is_alphabetic(U8 a) {
if ((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) {
return true;
}
return false;
}
function B32
is_number(U8 a) {
B32 result = a >= '0' && a <= '9';
return result;
}
function B32
is_alphanumeric(U8 a) {
B32 result = is_number(a) || is_alphabetic(a);
return result;
}
function B32
string_compare(String a, String b, B32 ignore_case = false) {
if (a.len != b.len)
return false;
for (S64 i = 0; i < a.len; i++) {
U8 A = a.str[i];
U8 B = b.str[i];
if (ignore_case) {
A = to_lower_case(A);
B = to_lower_case(B);
}
if (A != B)
return false;
}
return true;
}
function B32
operator==(String a, String b){
return string_compare(a,b);
}
function String
string_copy(Allocator *a, String string){
U8 *copy = exp_alloc_array(a, U8, string.len+1);
memory_copy(copy, string.str, string.len);
copy[string.len] = 0;
return (String){copy, string.len};
}
function String
string_fmtv(Allocator *a, const char *str, va_list args1) {
va_list args2;
va_copy(args2, args1);
S64 len = stbsp_vsnprintf(0, 0, str, args2);
va_end(args2);
char *result = exp_alloc_array(a, char, len + 1);
stbsp_vsnprintf(result, len + 1, str, args1);
String res = {(U8 *)result, len};
return res;
}
#define STRING_FMT(alloc, str, result) \
va_list args1; \
va_start(args1, str); \
String result = string_fmtv(alloc, str, args1); \
va_end(args1)
function String
string_fmt(Allocator *a, const char *str, ...) {
STRING_FMT(a, str, result);
return result;
}
//-----------------------------------------------------------------------------
// String builder
//-----------------------------------------------------------------------------
struct String_Builder_Block{
String_Builder_Block *next;
S64 cap;
S64 len;
U8 data[0];
};
struct String_Builder{
Allocator *allocator;
String_Builder_Block *first;
String_Builder_Block *last;
void push_block(SizeU size){
auto *block = (String_Builder_Block *)exp_alloc(allocator, sizeof(String_Builder_Block) + size);
memory_zero(block, sizeof(String_Builder_Block)+1); // Also clear first byte of character data
block->cap = size;
SLLQueuePush(first, last, block);
}
void init(S64 size = 4096){
assert(allocator);
push_block(size);
}
void append_data(void *data, S64 size){
if(first == 0){
init();
}
S64 remaining_cap = last->cap - last->len;
if(size > remaining_cap){
S64 new_block_size = max(last->cap*2, size*2);
push_block(new_block_size);
}
U8 *write_address = last->data + last->len;
last->len += size;
memory_copy(write_address, data, size);
}
};
function String_Builder
string_builder_make(Allocator *a, S64 first_block_size = 4096){
String_Builder sb = {a};
sb.init(first_block_size);
return sb;
}
function void
appendf(String_Builder *b, const char *str, ...){
if(b->first == 0){
b->init();
}
va_list args, args2;
va_start(args, str);
retry:{
String_Builder_Block *block = b->last;
int block_size = block->cap - block->len;
char *write_address = (char *)block->data + block->len;
va_copy(args2, args);
int written = vsnprintf(write_address, block_size, str, args2);
va_end(args2);
if(written > block_size){
int new_block_size = max(4096, (written+1)*2);
b->push_block(new_block_size);
goto retry;
}
block->len += written;
}
va_end(args);
}
enum String_Builder_Flag{
String_Builder_Flag_None = 0,
String_Builder_Flag_AddSize = 0,
};
function String
string_flatten(String_Builder *b, String_Builder_Flag flags = String_Builder_Flag_None){
// @Note(Krzosa): Only single block, no need to flatten, vsnprintf null terminates too
if(b->first == b->last){
String result = {b->first->data, b->first->len};
return result;
}
// @Note(Krzosa): Compute size to allocate
S64 size = 1;
if(is_flag_set(flags, String_Builder_Flag_AddSize)) size += sizeof(SizeU);
IterList(b){
size += it->len;
}
String result = {};
result.str = (U8 *)exp_alloc(b->allocator, size);
if(is_flag_set(flags, String_Builder_Flag_AddSize)) {
memory_copy(result.str + result.len, &size, sizeof(S64));
result.len += sizeof(S64);
}
// @Note(Krzosa): Copy the content of each block into the string
IterList(b){
memory_copy(result.str + result.len, it->data, it->len);
result.len += it->len;
}
result.str[result.len] = 0;
return result;
}
function void
test_string_builder(){
Scratch scratch;
String_Builder sb = string_builder_make(scratch, 4);
appendf(&sb, "Thing, %d", 242252);
String f = string_flatten(&sb);
assert(string_compare(f, "Thing, 242252"_s));
appendf(&sb, "-%f %f %f", 23.0, 42.29, 2925.2);
f = string_flatten(&sb);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
function void
string_path_normalize(String s) {
for (S64 i = 0; i < s.len; i++) {
if (s.str[i] == '\\')
s.str[i] = '/';
}
}
function String
string_make(char *str, S64 len) {
String result;
result.str = (U8 *)str;
result.len = len;
return result;
}
function String
string_make(U8 *str, S64 len) {
return string_make((char*)str, len);
}
function String
string_chop(String string, S64 len) {
len = clamp_top(len, string.len);
String result = string_make(string.str, string.len - len);
return result;
}
function String
string_skip(String string, S64 len) {
len = clamp_top(len, string.len);
S64 remain = string.len - len;
String result = string_make(string.str + len, remain);
return result;
}
function String
string_get_postfix(String string, S64 len) {
len = clamp_top(len, string.len);
S64 remain_len = string.len - len;
String result = string_make(string.str + remain_len, len);
return result;
}
function String
string_get_prefix(String string, S64 len) {
len = clamp_top(len, string.len);
String result = string_make(string.str, len);
return result;
}
function String
string_slice(String string, S64 first_index, S64 one_past_last_index) {
assert_msg(first_index < one_past_last_index, "string_slice, first_index is bigger then one_past_last_index");
assert_msg(string.len > 0, "Slicing string of length 0! Might be an error!");
String result = string;
if (string.len > 0) {
if (one_past_last_index > first_index) {
first_index = clamp_top(first_index, string.len - 1);
one_past_last_index = clamp_top(one_past_last_index, string.len);
result.str += first_index;
result.len = one_past_last_index - first_index;
}
else {
result.len = 0;
}
}
return result;
}
function String
string_trim(String string) {
if (string.len == 0) return string;
S64 whitespace_begin = 0;
for (; whitespace_begin < string.len; whitespace_begin++) {
if (!is_whitespace(string.str[whitespace_begin])) {
break;
}
}
S64 whitespace_end = string.len;
for (; whitespace_end != whitespace_begin; whitespace_end--) {
if (!is_whitespace(string.str[whitespace_end - 1])) {
break;
}
}
if (whitespace_begin == whitespace_end) {
string.len = 0;
}
else {
string = string_slice(string, whitespace_begin, whitespace_end);
}
return string;
}
function String
string_trim_end(String string) {
S64 whitespace_end = string.len;
for (; whitespace_end != 0; whitespace_end--) {
if (!is_whitespace(string.str[whitespace_end - 1])) {
break;
}
}
String result = string_get_prefix(string, whitespace_end);
return result;
}
function String
string_to_lower_case(Allocator *arena, String s) {
String copy = string_copy(arena, s);
for (U64 i = 0; i < copy.len; i++) {
copy.str[i] = to_lower_case(copy.str[i]);
}
return copy;
}
function String
string_to_upper_case(Allocator *arena, String s) {
String copy = string_copy(arena, s);
for (U64 i = 0; i < copy.len; i++) {
copy.str[i] = to_upper_case(copy.str[i]);
}
return copy;
}
typedef U64 MatchFlag;
enum {
MatchFlag_None=0,
MatchFlag_FindLast=1,
MatchFlag_IgnoreCase=2,
};
function B32
string_find(String string, String find, MatchFlag flags, S64 *index_out) {
B32 result = false;
if (flags & MatchFlag_FindLast) {
for (S64 i = string.len; i != 0; i--) {
S64 index = i - 1;
String substring = string_slice(string, index, index + find.len);
if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) {
if (index_out)
*index_out = index;
result = true;
break;
}
}
}
else {
for (S64 i = 0; i < string.len; i++) {
String substring = string_slice(string, i, i + find.len);
if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) {
if (index_out)
*index_out = i;
result = true;
break;
}
}
}
return result;
}
function String
string_chop_last_slash(String s) {
String result = s;
string_find(s, "/"_s, MatchFlag_FindLast, &result.len);
return result;
}
function String
string_chop_last_period(String s) {
String result = s;
string_find(s, "."_s, MatchFlag_FindLast, &result.len);
return result;
}
function String
string_skip_to_last_slash(String s) {
S64 pos;
String result = s;
if (string_find(s, "/"_s, MatchFlag_FindLast, &pos)) {
result = string_skip(result, pos + 1);
}
return result;
}
function String
string_skip_to_last_period(String s) {
S64 pos;
String result = s;
if (string_find(s, "."_s, MatchFlag_FindLast, &pos)) {
result = string_skip(result, pos + 1);
}
return result;
}
#if 0
function void
test_stb_sprintf(){
char buff[1024];
const char *asd = "adfag";
int len = snprintf(buff, 1024, "Thing %s", asd);
assert(string_make(buff,len) == "Thing adfag"_s);
String thing = "Thing12312412412412412"_s;
thing.len = 5;
len = snprintf(buff, 1024, "%Q %Q", thing, string_make(thing.str + 5, 5));
__debugbreak();
}
#endif

View File

@@ -1,4 +1,4 @@
@echo off @echo off
clang main.cpp -Wall -Wno-unused-function -Wno-missing-braces -fno-exceptions -fdiagnostics-absolute-paths -g -I"C:/base" -I".." -o main.exe -Wl,user32.lib clang main.cpp -Wall -Wno-unused-function -Wno-missing-braces -fno-exceptions -fdiagnostics-absolute-paths -g -I".." -o main.exe -Wl,user32.lib

View File

@@ -1,899 +0,0 @@
#include <Windows.h>
#include <shellscalingapi.h>
#include <GL/Gl.h>
#include <intrin.h>
#pragma comment(linker, "/subsystem:windows")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "Shcore.lib")
#pragma comment(lib, "opengl32.lib")
// Symbols taken from GLFW
//
// Executables (but not DLLs) exporting this symbol with this value will be
// automatically directed to the high-performance GPU on Nvidia Optimus systems
// with up-to-date drivers
//
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
// Executables (but not DLLs) exporting this symbol with this value will be
// automatically directed to the high-performance GPU on AMD PowerXpress systems
// with up-to-date drivers
//
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
OS os;
#include "math.h"
typedef HRESULT tSetProcessDpiAwareness(PROCESS_DPI_AWARENESS);
typedef MMRESULT TimeBeginPeriod(MMRESULT);
constexpr DWORD window_style_simplified = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
constexpr DWORD window_style_resizable = WS_OVERLAPPEDWINDOW;
constexpr DWORD window_style_borderless = WS_POPUP;
struct Win32Bitmap {
Bitmap bitmap;
HDC dc;
HBITMAP dib;
};
struct Win32FontCtx {
HFONT font;
Win32Bitmap bitmap;
TEXTMETRIC text_metric;
Font result;
};
struct OS_Win32 {
HWND window;
HDC window_dc;
Win32Bitmap screen;
HINSTANCE instance;
int show_cmd;
char *cmd_line;
bool good_scheduling;
void *main_fiber;
void *msg_fiber;
U8 text_buff[32];
Vec2I prev_window_size;
Vec2I prev_mouse_pos;
};
static_assert(sizeof(OS_Win32) < 256, "Too big");
#define w32(a) (*(OS_Win32 *)(a).platform)
api Bitmap bitmap(U32 *pixels, Vec2I size, Vec2 align_in_pixels = {}) {
Bitmap result;
result.pixels = pixels;
result.size = size;
result.align = align_in_pixels;
return result;
}
function Win32Bitmap win32_create_bitmap(Vec2I size) {
Win32Bitmap result;
result.bitmap.size = size;
if (result.bitmap.size.y < 0)
result.bitmap.size.y = -result.bitmap.size.y;
HDC hdc = GetDC(0);
BITMAPINFO bminfo = {};
bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
bminfo.bmiHeader.biWidth = (LONG)size.x;
bminfo.bmiHeader.biHeight = (LONG)-size.y;
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB; // AA RR GG BB
bminfo.bmiHeader.biXPelsPerMeter = 1;
bminfo.bmiHeader.biYPelsPerMeter = 1;
void *mem = 0;
result.dib = CreateDIBSection(hdc, &bminfo, DIB_RGB_COLORS, (void **)&mem, 0, 0);
assert_msg(mem != 0, "Failed to create win32 bitmap");
result.dc = CreateCompatibleDC(hdc);
result.bitmap.pixels = (U32 *)mem;
return result;
}
function void win32_destroy_bitmap(Win32Bitmap *b) {
if (b->bitmap.pixels) {
b->bitmap.pixels = 0;
DeleteDC(b->dc);
DeleteObject(b->dib);
}
}
function LRESULT
CALLBACK win32_window_proc(HWND window, UINT uMsg, WPARAM wParam, LPARAM lParam) {
EventKind kind = EventKind_None;
Key key = Key_None;
LRESULT result = 0;
switch (uMsg) {
case WM_CLOSE:
DestroyWindow(window);
os.quit = 1;
break;
case WM_DESTROY:
PostQuitMessage(0);
os.quit = 1;
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN: {
kind = EventKind_KeyDown;
switch (wParam) {
#define X(button, win) \
case win: \
key = Key_##button; \
break;
KEY_MAPPING
}
} break;
case WM_SYSKEYUP:
case WM_KEYUP: {
kind = EventKind_KeyUp;
switch (wParam) {
KEY_MAPPING
#undef X
}
} break;
case WM_TIMER: {
SwitchToFiber(w32(os).main_fiber);
} break;
case WM_ENTERMENULOOP:
case WM_ENTERSIZEMOVE: {
SetTimer(w32(os).window, 0, 1, 0);
} break;
case WM_EXITMENULOOP:
case WM_EXITSIZEMOVE: {
KillTimer(w32(os).window, 0);
} break;
case WM_MOUSEMOVE: {
POINT p;
GetCursorPos(&p);
ScreenToClient(w32(os).window, &p);
os.mouse_pos = vec2i(p.x, p.y);
os.mouse_pos.y = os.window_size.y - os.mouse_pos.y;
kind = EventKind_MouseMove;
} break;
case WM_LBUTTONDOWN: {
kind = EventKind_KeyDown;
key = Key_MouseLeft;
} break;
case WM_LBUTTONUP: {
kind = EventKind_KeyUp;
key = Key_MouseLeft;
} break;
case WM_RBUTTONDOWN: {
kind = EventKind_KeyDown;
key = Key_MouseRight;
} break;
case WM_RBUTTONUP: {
kind = EventKind_KeyUp;
key = Key_MouseRight;
} break;
case WM_MBUTTONDOWN: {
kind = EventKind_KeyDown;
key = Key_MouseMiddle;
} break;
case WM_MBUTTONUP: {
kind = EventKind_KeyUp;
key = Key_MouseMiddle;
} break;
case WM_MOUSEWHEEL: {
if ((int)wParam > 0)
os.mouse_wheel = 1;
else
os.mouse_wheel = -1;
kind = EventKind_MouseWheel;
} break;
case WM_CHAR: {
// @Todo: Can this overflow?
//kind = EventKind_KeyboardText;
//U32 codepoint = '?';
//utf16_to_utf32((U16 *)&wParam, &codepoint);
//os.text.len += utf32_to_utf8(codepoint, w32(os).text_buff + os.text.len);
} break;
default:
result = DefWindowProcW(window, uMsg, wParam, lParam);
break;
}
if (kind) {
if (kind == EventKind_KeyDown) {
if (os.key[key].down == 0)
os.key[key].pressed = 1;
os.key[key].down = 1;
} else if (kind == EventKind_KeyUp) {
os.key[key].released = 1;
os.key[key].down = 0;
}
}
return result;
}
function void
CALLBACK _os_fiber_event_proc(void *data) {
unused(data);
for (;;) {
MSG msg;
while (PeekMessageW(&msg, w32(os).window, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
SwitchToFiber(w32(os).main_fiber);
}
}
function Vec2I
get_window_size(HWND window) {
Vec2I result;
RECT window_rect;
GetClientRect(window, &window_rect);
result.x = window_rect.right - window_rect.left;
result.y = window_rect.bottom - window_rect.top;
return result;
}
function Vec2I
get_window_size_with_border(HWND window) {
RECT ClientRect;
GetWindowRect(window, &ClientRect);
Vec2I draw_area;
draw_area.x = (ClientRect.right - ClientRect.left);
draw_area.y = (ClientRect.bottom - ClientRect.top);
return draw_area;
}
function Vec2I
get_border_size(HWND window) {
Vec2I client = get_window_size(window);
Vec2I wind_size = get_window_size_with_border(window);
Vec2I result = vec2i(wind_size.x - client.x, wind_size.y - client.y);
return result;
}
api void os_quit() { os.quit = 1; }
api void os_set_fps(F64 fps) { os.ms_per_frame = 1 / fps; }
api void os_set_ms_per_frame(F64 ms) { os.ms_per_frame = ms; }
api void os_show_cursor(B32 status) {
ShowCursor(status);
os.cursor_visible = status;
}
api void os_set_window_title(String title) {
Scratch scratch;
BOOL result = SetWindowTextA(w32(os).window, (char *)title.str);
assert_msg(result != 0, "Failed to set window title");
os.window_title = title;
}
api void os_set_window_size(S32 x, S32 y) {
Vec2I border = get_border_size(w32(os).window);
int actual_width = (int)(x + border.x);
int actual_height = (int)(y + border.y);
bool result =
SetWindowPos(w32(os).window, 0, 0, 0, actual_width, actual_height, SWP_NOMOVE | SWP_NOOWNERZORDER);
assert_msg(result, "SetWindowPos returned invalid value");
os.window_size = get_window_size(w32(os).window);
}
api void os_pull_state() {
os.window_was_resized = false;
os.window_size = get_window_size(w32(os).window);
if (os.window_size != w32(os).prev_window_size) {
os.window_was_resized = true;
w32(os).prev_window_size = os.window_size;
}
os.monitor_size = vec2i(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
// @Note: Client relative
POINT point = {0, 0};
ClientToScreen(w32(os).window, &point);
os.window_pos.x = point.x;
os.window_pos.y = point.y;
// @Note: Get DPI scale
UINT dpi = GetDpiForWindow(w32(os).window);
assert_msg(dpi != 0, "Failed to get dpi for window");
os.dpi_scale = (F32)dpi / 96.f;
// @Note: Reset text
os.text.len = 0;
// @Note: Reset keys
for (int i = 0; i < Key_Count; i++) {
os.key[i].released = 0;
os.key[i].pressed = 0;
}
os.mouse_wheel = 0;
SwitchToFiber(w32(os).msg_fiber);
if (!os.quit) {
os.delta_mouse_pos = w32(os).prev_mouse_pos - os.mouse_pos;
w32(os).prev_mouse_pos = os.mouse_pos;
// @Note: Resize
if (os.render_backend == RenderBackend_Software) {
if (os.window_size != w32(os).screen.bitmap.size && os.window_size.x != 0 && os.window_size.y != 0) {
win32_destroy_bitmap(&w32(os).screen);
w32(os).screen = win32_create_bitmap(vec2i(os.window_size.x, -(os.window_size.y)));
os.screen = &w32(os).screen.bitmap;
}
}
}
}
api void os_init_software_render() { os.render_backend = RenderBackend_Software; }
api B32 os_init_opengl() {
PIXELFORMATDESCRIPTOR p = {};
p.nSize = sizeof(p);
p.nVersion = 1;
p.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
p.iPixelType = PFD_TYPE_RGBA;
p.cColorBits = 32;
p.cDepthBits = 24;
p.cStencilBits = 8;
p.iLayerType = PFD_MAIN_PLANE;
S32 pixel_format = ChoosePixelFormat(w32(os).window_dc, &p);
if (pixel_format != 0) {
if (SetPixelFormat(w32(os).window_dc, pixel_format, &p)) {
HGLRC gl_ctx = wglCreateContext(w32(os).window_dc);
if (gl_ctx != NULL) {
if (wglMakeCurrent(w32(os).window_dc, gl_ctx)) {
// Success
}
else {
log_error("Failed on wglMakeCurrent!");
return false;
}
}
else {
log_error("Failed on wglCreateContext!");
return false;
}
}
else {
log_error("Failed on SetPixelFormat!");
return false;
}
}
else {
log_error("Failed on ChoosePixelFormat!");
return false;
}
os.opengl.vendor = (char *)glGetString(GL_VENDOR);
os.opengl.renderer = (char *)glGetString(GL_RENDERER);
os.opengl.version = (char *)glGetString(GL_VERSION);
os.opengl.extensions = (char *)glGetString(GL_EXTENSIONS);
os.render_backend = RenderBackend_OpenGL1;
return true;
}
global S64 Global_counts_per_second;
api F64 os_time() {
if (Global_counts_per_second == 0) {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
Global_counts_per_second = freq.QuadPart;
}
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
F64 result = (F64)time.QuadPart / (F64)Global_counts_per_second;
return result;
}
api B32 os_init() {
HMODULE shcore = LoadLibraryA("Shcore.dll");
if (shcore) {
tSetProcessDpiAwareness *set_dpi_awr =
(tSetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness");
if (set_dpi_awr) {
HRESULT hr = set_dpi_awr(PROCESS_PER_MONITOR_DPI_AWARE);
assert_msg(SUCCEEDED(hr), "Failed to set dpi awareness");
}
}
HMODULE winmm = LoadLibraryA("winmm.dll");
if (winmm) {
TimeBeginPeriod *timeBeginPeriod = (TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod");
if (timeBeginPeriod) {
if (timeBeginPeriod(1) == TIMERR_NOERROR) {
w32(os).good_scheduling = true;
}
}
}
DWORD window_style_chosen = window_style_resizable;
if (!os.window_resizable)
window_style_chosen = window_style_simplified;
os.app_start_time = os_time();
os.frame_start_time = os.app_start_time;
os.update_begin_cycles = __rdtsc();
WNDCLASSW wc = {}; // @Todo(Krzosa): UTF
wc.lpfnWndProc = win32_window_proc;
wc.hInstance = w32(os).instance;
// @Todo(Krzosa): UTF conversions
wc.lpszClassName = L"Have a good day"; // @Todo(Krzosa): Cant set window title
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
if (!RegisterClassW(&wc)) { // @Todo(Krzosa): UTF
log_error("Failed to create window class!");
return false;
}
RECT window_rect;
window_rect.left = (LONG)os.window_pos.x;
window_rect.top = (LONG)os.window_pos.y;
window_rect.right = (LONG)os.window_size.x + window_rect.left;
window_rect.bottom = (LONG)os.window_size.y + window_rect.top;
AdjustWindowRectEx(&window_rect, window_style_chosen, false, 0);
// @Todo(Krzosa): UTF
w32(os).window = CreateWindowW(wc.lpszClassName, wc.lpszClassName, window_style_chosen, window_rect.left, window_rect.top, window_rect.right - window_rect.left,window_rect.bottom - window_rect.top, NULL, NULL, w32(os).instance, NULL);
if (w32(os).window == 0) {
log_error("Failed to create window!");
return false;
}
ShowWindow(w32(os).window, w32(os).show_cmd);
UpdateWindow(w32(os).window);
w32(os).window_dc = GetDC(w32(os).window);
w32(os).main_fiber = ConvertThreadToFiber(0);
assert_msg(w32(os).main_fiber, "Failed to create main fiber");
w32(os).msg_fiber = CreateFiber(0, _os_fiber_event_proc, 0);
assert_msg(w32(os).msg_fiber, "Failed to create message fiber");
if (os.cursor_visible == false)
os_show_cursor(false);
switch (os.render_backend) {
case RenderBackend_Software: {
os_init_software_render();
} break;
case RenderBackend_OpenGL1: {
os_init_opengl();
} break;
default: assert_msg(0, "Invalid value for render backend");
break;
}
os_pull_state();
os.initialized = true;
return true;
}
api bool os_game_loop() {
assert_msg(os.initialized, "Platform is not initialized! Please call os_init");
switch (os.render_backend) {
case RenderBackend_Software: {
if (os.screen) { // @Note: Draw screen
U32 *p = os.screen->pixels;
for (int y = 0; y < os.screen->y; y++) {
for (int x = 0; x < os.screen->x; x++) {
*p = ((*p & 0xff000000)) | ((*p & 0x00ff0000) >> 16) | ((*p & 0x0000ff00)) |
((*p & 0x000000ff) << 16);
p += 1;
}
}
HDC hdc = w32(os).window_dc;
SelectObject(w32(os).screen.dc, w32(os).screen.dib);
BitBlt(hdc, 0, 0, (LONG)os.screen->size.x, (LONG)os.screen->size.y, w32(os).screen.dc, 0, 0, SRCCOPY);
}
} break;
case RenderBackend_OpenGL1: {
SwapBuffers(w32(os).window_dc);
} break;
default:
assert_msg(0, "Please select a rendering backend!");
break;
}
arena_clear(os.frame_arena);
os.update_time = os_time() - os.frame_start_time;
os.update_end_cycles = __rdtsc();
F64 frame_time = os.update_time;
F64 ms_per_frame = os.ms_per_frame;
if (frame_time < ms_per_frame) {
if (w32(os).good_scheduling) {
// @Todo: I have no idea if letting over sleep is bad or not
// Busy waiting is chugging cpu alot more, not sure what to do
F64 time_to_sleep = (ms_per_frame - frame_time) * 1000;
if (time_to_sleep > 0) {
Sleep((DWORD)time_to_sleep);
}
}
do {
frame_time = os_time() - os.frame_start_time;
} while (frame_time < ms_per_frame);
}
os.frame++;
os.delta_time = frame_time;
os.fps = 1 / os.delta_time;
os.time += os.delta_time;
os.frame_start_time = os_time();
os.update_begin_cycles = __rdtsc();
os_pull_state();
return !os.quit;
}
int main(int argc, char **argv);
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmd_line, int show_cmd) {
thread_ctx_init();
w32(os).instance = instance;
w32(os).show_cmd = show_cmd;
w32(os).cmd_line = cmd_line;
Arena frame_arena = {};
arena_init(&frame_arena, "Frame arena"_s);
os.perm_arena = &pernament_arena;
os.frame_arena = &frame_arena;
os.dpi_scale = 1;
os.text.str = w32(os).text_buff;
os.window_title = "Have a good day!"_s;
os.window_pos.x = 0;
os.window_pos.y = 50;
os.window_size.x = 1280;
os.window_size.y = 720;
os.ms_per_frame = 1.f / 60.f;
os.cursor_visible = true;
if(AttachConsole(-1)) {
freopen("CONIN$", "r",stdin);
freopen("CONOUT$", "w",stdout);
freopen("CONOUT$", "w",stderr);
}
return main(__argc, __argv);
}
//////////////
// @Note: Font API
function FontGlyph extract_glyph(Allocator *arena, Win32FontCtx *ctx, wchar_t glyph) {
SIZE size;
GetTextExtentPoint32W(ctx->bitmap.dc, &glyph, 1, &size);
TextOutW(ctx->bitmap.dc, 0, 0, &glyph, 1);
// @Note: Find bitmap edges
int minx = 100000;
int miny = 100000;
int maxx = -100000;
int maxy = -100000;
for (int y = 499; y >= 500 - size.cy; y--) {
for (int x = 0; x < size.cx; x++) {
if (ctx->bitmap.bitmap.pixels[x + y * (int)ctx->bitmap.bitmap.size.x] != 0) {
if (minx > x)
minx = x;
if (miny > y)
miny = y;
if (maxx < x)
maxx = x;
if (maxy < y)
maxy = y;
}
}
}
assert(minx >= 0 && miny >= 0);
int bwidth = maxx - minx + 1;
int bheight = maxy - miny + 1;
U32 *cropped =
(U32 *)exp_alloc(arena, sizeof(U32) * (U32)(bwidth) * (U32)(bheight));
for (int y = miny; y <= maxy; y++) {
for (int x = minx; x <= maxx; x++) {
U32 value = ctx->bitmap.bitmap.pixels[x + y * (int)ctx->bitmap.bitmap.size.x];
U32 *dst = cropped + ((size_t)(x - minx) + (size_t)(y - miny) * bwidth);
#if 1 // Premultiplied alpha
F32 alpha = (F32)((value & 0x000000ff) >> 0);
F32 rgb = (F32)0xff*(alpha/255.f);
U8 val = (U8)(rgb + 0.5f);
*dst = (((U32)(alpha+0.5f) << 24) | val << 16 | val << 8 | val);
#else
U8 grey = (value & 0x000000ff);
*dst = (grey << 24 | 0xff << 16 | 0xff << 8 | 0xff);
#endif
}
}
exp_alloc(arena, sizeof(U32) * (U32)bwidth);
// @Note: Calculate char metrics
int glyph_descent = (499 - size.cy) - miny;
FontGlyph result;
INT width;
GetCharWidth32W(ctx->bitmap.dc, glyph, glyph, &width);
result.xadvance = (F32)width;
result.bitmap = bitmap(cropped, vec2i(bwidth, bheight),
vec2((F32)-minx, (F32)ctx->text_metric.tmDescent + (F32)glyph_descent));
return result;
}
function Win32FontCtx begin_font_extraction(char *filename, char *font_name, S64 height) {
assert_msg(height < 500, "Height of font over 500");
Win32FontCtx ctx = {};
if (filename) {
int fonts_added = AddFontResourceExA(filename, FR_PRIVATE, 0);
assert_msg(fonts_added != 0, "AddFontResourceEx added 0 fonts");
}
ctx.bitmap = win32_create_bitmap(vec2i(500, -500));
ctx.font =
CreateFontA((S32)height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, font_name);
assert_msg(ctx.font != NULL, "CreateFont returned a 0 pointer");
SelectObject(ctx.bitmap.dc, ctx.bitmap.dib);
SelectObject(ctx.bitmap.dc, ctx.font);
GetTextMetrics(ctx.bitmap.dc, &ctx.text_metric);
ctx.result.height = (F32)height;
ctx.result.descent = (F32)ctx.text_metric.tmDescent;
ctx.result.ascent = (F32)ctx.text_metric.tmAscent;
ctx.result.line_advance = (F32)(height + ctx.text_metric.tmExternalLeading);
SetBkColor(ctx.bitmap.dc, RGB(0, 0, 0));
SetTextColor(ctx.bitmap.dc, RGB(255, 255, 255));
return ctx;
}
function Font end_font_extraction(Win32FontCtx *ctx) {
win32_destroy_bitmap(&ctx->bitmap);
DeleteObject(ctx->font);
return ctx->result;
}
api Font os_load_font(Allocator *arena, S32 height, const char *font_name, const char *filename) {
FontGlyph *glyphs = exp_alloc_array(arena, FontGlyph, 96, AF_ZeroMemory);
Win32FontCtx font_ctx = begin_font_extraction((char *)filename, (char *)font_name, height);
for (U32 i = '!'; i <= '~'; i++) {
glyphs[i - '!'] = extract_glyph(arena, &font_ctx, (wchar_t)i);
}
Font font = end_font_extraction(&font_ctx);
font.glyphs = glyphs;
font.glyphs_len = '~' - '!';
return font;
}
function void set_window_style(HWND window, DWORD style) {
// @Todo: Need to readup on this
SetWindowLongPtrW(window, GWL_STYLE, style);
SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(window, SW_SHOW);
}
#undef w32
///////////////////////////////////////
// @Section Audio
#include <objbase.h>
#include <audioclient.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
// NOTE: typedefines for the functions which are goint to be loaded
typedef HRESULT CoCreateInstanceFunction(REFCLSID rclsid, LPUNKNOWN *pUnkOuter, DWORD dwClsContext,
REFIID riid, LPVOID *ppv);
typedef HRESULT CoInitializeExFunction(LPVOID pvReserved, DWORD dwCoInit);
// NOTE: empty functions(stubs) which are used when library fails to load
HRESULT CoCreateInstanceStub(REFCLSID rclsid, LPUNKNOWN *pUnkOuter, DWORD dwClsContext, REFIID riid,
LPVOID *ppv) {
unused(rclsid);
unused(pUnkOuter);
unused(dwClsContext);
unused(riid);
unused(ppv);
return S_FALSE;
}
HRESULT CoInitializeExStub(LPVOID pvReserved, DWORD dwCoInit) {
unused(pvReserved);
unused(dwCoInit);
return S_FALSE;
}
// NOTE: pointers to the functions from the dll
CoCreateInstanceFunction *CoCreateInstanceFunctionPointer = CoCreateInstanceStub;
CoInitializeExFunction *CoInitializeExFunctionPointer = CoInitializeExStub;
// NOTE: Number of REFERENCE_TIME units per second
// One unit is equal to 100 nano seconds
#define REF_TIMES_PER_SECOND 10000000
#define REF_TIMES_PER_MSECOND 10000
#define w32(a) (*(Audio_Win32 *)(a)->platform)
struct Audio_Win32 {
IMMDevice *device;
IAudioClient *audio_client;
IMMDeviceEnumerator *device_enum;
IAudioRenderClient *audio_render_client;
IAudioCaptureClient *audio_capture_client;
};
static_assert(sizeof(Audio::platform) > sizeof(Audio_Win32),
"Audio::platform is too small to hold Audio_Win32 struct");
// Load COM Library functions dynamically,
// this way sound is not necessary to run the game
function B32 win32_load_com() {
B32 result = true;
HMODULE ole32Library = LoadLibraryA("ole32.dll");
if (ole32Library) {
CoCreateInstanceFunctionPointer =
(CoCreateInstanceFunction *)GetProcAddress(ole32Library, "CoCreateInstance");
if (!CoCreateInstanceFunctionPointer) {
CoCreateInstanceFunctionPointer = CoCreateInstanceStub;
log_error("CoCreateInstance failed to load");
result = false;
}
CoInitializeExFunctionPointer = (CoInitializeExFunction *)GetProcAddress(ole32Library, "CoInitializeEx");
if (!CoInitializeExFunctionPointer) {
CoInitializeExFunctionPointer = CoInitializeExStub;
log_error("CoInitializeEx failed to load");
result = false;
}
} else {
CoCreateInstanceFunctionPointer = CoCreateInstanceStub;
CoInitializeExFunctionPointer = CoInitializeExStub;
log_error("Failed to load OLE32.dll");
result = false;
}
return result;
}
api void os_clean_audio(Audio *audio) {
if (w32(audio).audio_client)
w32(audio).audio_client->Stop();
if (w32(audio).device_enum)
w32(audio).device_enum->Release();
if (w32(audio).device)
w32(audio).device->Release();
if (w32(audio).audio_client)
w32(audio).audio_client->Release();
if (w32(audio).audio_render_client)
w32(audio).audio_render_client->Release();
audio->initialized = false;
}
function DWORD win32_audio_thread(void *parameter) {
Audio *audio = (Audio *)parameter;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
HANDLE buffer_ready_event = CreateEvent(0, 0, 0, 0);
if (!buffer_ready_event) {
return -1;
}
if (FAILED(w32(audio).audio_client->SetEventHandle(buffer_ready_event))) {
return -1;
}
U32 buffer_frame_count;
if (FAILED(w32(audio).audio_client->GetBufferSize(&buffer_frame_count))) {
return -1;
}
// U32 buffer_sample_count = buffer_frame_count * audio->number_of_channels;
if (FAILED(w32(audio).audio_client->Start())) {
w32(audio).audio_client->Stop();
return -1;
}
for (;;) {
if (WaitForSingleObject(buffer_ready_event, INFINITE) != WAIT_OBJECT_0) {
w32(audio).audio_client->Stop();
return -1;
}
U32 padding_frame_count;
if (FAILED(w32(audio).audio_client->GetCurrentPadding(&padding_frame_count))) {
w32(audio).audio_client->Stop();
return -1;
}
U32 *samples;
U32 fill_frame_count = buffer_frame_count - padding_frame_count;
if (FAILED(w32(audio).audio_render_client->GetBuffer(fill_frame_count, (BYTE **)&samples))) {
w32(audio).audio_client->Stop();
return -1;
}
audio->callback(audio, samples, fill_frame_count);
if (FAILED(w32(audio).audio_render_client->ReleaseBuffer(fill_frame_count, 0))) {
w32(audio).audio_client->Stop();
return -1;
}
}
return 0;
}
function AUDIO_CALLBACK(default_audio_callback) {
memory_zero(buffer, (U64)frames_to_fill * (U64)audio->samples_per_second);
}
api B32 os_init_audio(Audio *audio) {
audio->bits_per_sample = 16;
if (audio->number_of_channels == 0)
audio->number_of_channels = 2;
if (audio->samples_per_second == 0)
audio->samples_per_second = 44100;
if (audio->callback == 0)
audio->callback = default_audio_callback;
B32 success = win32_load_com();
if (!success) {
return false;
}
if (FAILED(CoInitializeExFunctionPointer(0, COINITBASE_MULTITHREADED))) {
log_error("Failed to initialize COM, CoInitializeEx");
return false;
}
if (FAILED(CoCreateInstanceFunctionPointer(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
IID_IMMDeviceEnumerator, (LPVOID *)&w32(audio).device_enum))) {
log_error("Failed to initialize COM, CoCreateInstance");
return false;
}
if (FAILED(w32(audio).device_enum->GetDefaultAudioEndpoint(eRender, eConsole, &w32(audio).device))) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, GetDefaultAudioEndpoint");
return false;
}
if (FAILED(
w32(audio).device->Activate(IID_IAudioClient, CLSCTX_ALL, 0, (void **)&w32(audio).audio_client))) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, "
"w32(audio).device->Activate(IID_IAudioClient,");
return false;
}
WAVEFORMATEX waveFormat = {};
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = audio->number_of_channels;
waveFormat.nSamplesPerSec = audio->samples_per_second;
waveFormat.wBitsPerSample = audio->bits_per_sample;
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
REFERENCE_TIME requestedBufferDuration = REF_TIMES_PER_MSECOND * 40;
if (FAILED(w32(audio).audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_RATEADJUST |
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
requestedBufferDuration, 0, &waveFormat, 0))) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, w32(audio).audio_client->Initialize");
return false;
}
if (FAILED(w32(audio).audio_client->GetService(IID_IAudioRenderClient,
(void **)&w32(audio).audio_render_client))) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, "
"w32(audio).audio_client->GetService(IID_IAudioRenderClient");
return false;
}
if (FAILED(w32(audio).audio_client->GetBufferSize(&audio->buffer_frame_count))) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, w32(audio).audio_client->GetBufferSize");
return false;
}
HANDLE thread_handle = CreateThread(0, 0, win32_audio_thread, audio, 0, 0);
if (!thread_handle) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, CreateThread returned 0 in handle");
return false;
}
if (thread_handle == INVALID_HANDLE_VALUE) {
os_clean_audio(audio);
log_error("Failed to initialize WASAPI, CreateThread for "
"audio failed with INVALID HANDLE VALUE");
return false;
}
CloseHandle(thread_handle);
audio->initialized = true;
#undef w32
return true;
}

View File

@@ -1,230 +0,0 @@
#pragma once
struct Bitmap {
union {
U32 *pixels;
U64 id;
};
union {
Vec2I size;
struct {
S32 x, y;
};
};
Vec2 align;
};
enum EventKind {
EventKind_None,
EventKind_KeyDown,
EventKind_KeyUp,
EventKind_MouseMove,
EventKind_MouseWheel,
EventKind_KeyboardText,
};
enum Key {
Key_None,
Key_Up,
Key_Down,
Key_Left,
Key_Right,
Key_Escape,
Key_F1,
Key_F2,
Key_F3,
Key_F4,
Key_F5,
Key_F6,
Key_F7,
Key_F8,
Key_F9,
Key_F10,
Key_F11,
Key_F12,
Key_MouseLeft,
Key_MouseRight,
Key_MouseMiddle,
Key_0 = '0',
Key_1,
Key_2,
Key_3,
Key_4,
Key_5,
Key_6,
Key_7,
Key_8,
Key_9 = '9',
Key_A = 'a',
Key_B,
Key_C,
Key_D,
Key_E,
Key_F,
Key_G,
Key_H,
Key_I,
Key_J,
Key_K,
Key_L,
Key_M,
Key_N,
Key_O,
Key_P,
Key_Q,
Key_R,
Key_S,
Key_T,
Key_U,
Key_V,
Key_W,
Key_X,
Key_Y,
Key_Z = 'z',
Key_Count = 256,
};
#define KEY_MAPPING \
X(Up, VK_UP) \
X(Down, VK_DOWN) \
X(Left, VK_LEFT) \
X(Right, VK_RIGHT) \
X(Escape, VK_ESCAPE) \
X(F1, VK_F1) \
X(F2, VK_F2) \
X(F3, VK_F3) \
X(F4, VK_F4) \
X(F5, VK_F5) \
X(F6, VK_F6) \
X(F7, VK_F7) \
X(F8, VK_F8) \
X(F9, VK_F9) \
X(F10, VK_F10) \
X(F11, VK_F11) \
X(F12, VK_F12) \
X(A, 65) \
X(B, 66) \
X(C, 67) \
X(D, 68) \
X(E, 69) \
X(F, 70) \
X(G, 71) \
X(H, 72) \
X(I, 73) \
X(J, 74) \
X(K, 75) \
X(L, 76) \
X(M, 77) \
X(N, 78) \
X(O, 79) \
X(P, 80) \
X(Q, 81) \
X(R, 82) \
X(S, 83) \
X(T, 84) \
X(U, 85) \
X(V, 86) \
X(W, 87) \
X(X, 88) \
X(Y, 89) \
X(Z, 90) \
X(0, 48) \
X(1, 49) \
X(2, 50) \
X(3, 51) \
X(4, 52) \
X(5, 53) \
X(6, 54) \
X(7, 55) \
X(8, 56) \
X(9, 57)
struct DigitalKey {
bool pressed;
bool down;
bool released;
};
enum RenderBackend {
RenderBackend_Software,
RenderBackend_OpenGL1,
};
struct OS {
bool quit;
bool initialized;
Arena *frame_arena;
Arena *perm_arena;
F64 ms_per_frame;
bool window_resizable;
bool window_was_resized;
String window_title;
Vec2I window_size;
Vec2I window_pos;
RenderBackend render_backend;
Bitmap *screen;
Vec2I monitor_size;
F32 dpi_scale;
F64 fps;
F64 delta_time;
F64 time;
U64 frame;
F64 update_time;
U64 update_begin_cycles;
U64 update_end_cycles;
F64 frame_start_time;
F64 app_start_time;
B32 cursor_visible;
DigitalKey key[Key_Count];
F32 mouse_wheel;
Vec2I mouse_pos;
Vec2I delta_mouse_pos;
String text;
char platform[256];
struct {
char *vendor;
char *version;
char *renderer;
char *extensions;
} opengl;
};
struct FontGlyph {
F32 xadvance;
U32 codepoint;
Bitmap bitmap;
};
struct Font {
F32 line_advance;
F32 ascent;
F32 descent;
F32 height;
FontGlyph *glyphs;
U32 glyphs_len;
};
struct Audio;
#define AUDIO_CALLBACK(name) void name(Audio *audio, U32* buffer, U32 frames_to_fill)
typedef AUDIO_CALLBACK(AudioCallback);
struct Audio {
AudioCallback *callback;
B32 initialized;
U32 samples_per_second;
U32 number_of_channels;
// NOTE: one frame is 2 samples (left, right) 32 bits if
// one sample is equal 16 bit
U32 buffer_frame_count;
U32 latency_frame_count;
S32 bits_per_sample;
char platform[128];
};

View File

@@ -78,21 +78,24 @@
/// - [x] Simple scatter plot /// - [x] Simple scatter plot
/// ///
/// ///
/// ### Urgent:
///
/// - [ ] Simplify the code, especially for the 2d routines
/// - [ ] Asset processor as second program
///
///
#define PREMULTIPLIED_ALPHA_BLENDING 1 #define PREMULTIPLIED_ALPHA_BLENDING 1
#include "base.cpp" #include "multimedia.cpp"
#include "kpl_multimedia.h"
#include "kpl_multimedia.cpp"
#include "profile.cpp" #include "profile.cpp"
#include "math.h"
struct R_Vertex { struct Vertex {
Vec3 pos; Vec3 pos;
Vec2 tex; Vec2 tex;
Vec3 norm; Vec3 norm;
}; };
struct R_Render { struct Render {
Mat4 camera; Mat4 camera;
Mat4 projection; Mat4 projection;
Mat4 transform; Mat4 transform;
@@ -109,6 +112,7 @@ struct R_Render {
F32 *depth320; F32 *depth320;
}; };
#define STBI_ASSERT assert
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" #include "stb_image.h"
#include "obj_parser.cpp" #include "obj_parser.cpp"
@@ -145,7 +149,7 @@ Vec4 premultiplied_alpha(Vec4 dst, Vec4 src) {
} }
function function
void r_draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) { void draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) {
int max_x = (int)(min(X + w, (F32)dst->x) + 0.5f); int max_x = (int)(min(X + w, (F32)dst->x) + 0.5f);
int max_y = (int)(min(Y + h, (F32)dst->y) + 0.5f); int max_y = (int)(min(Y + h, (F32)dst->y) + 0.5f);
int min_x = (int)(max(0.f, X) + 0.5f); int min_x = (int)(max(0.f, X) + 0.5f);
@@ -165,7 +169,7 @@ void r_draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) {
} }
function function
void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F32MAX)) { void draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F32MAX)) {
S64 minx = (S64)(pos.x + 0.5); S64 minx = (S64)(pos.x + 0.5);
S64 miny = (S64)(pos.y + 0.5); S64 miny = (S64)(pos.y + 0.5);
@@ -247,7 +251,7 @@ void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F3
} }
function function
Vec4 r_base_string(Bitmap *dst, Font *font, String word, Vec2 pos, B32 draw) { Vec4 base_string(Bitmap *dst, Font *font, String word, Vec2 pos, B32 draw) {
Vec2 og_position = pos; Vec2 og_position = pos;
F32 max_x = pos.x; F32 max_x = pos.x;
for (U64 i = 0; i < word.len; i++) { for (U64 i = 0; i < word.len; i++) {
@@ -262,7 +266,7 @@ Vec4 r_base_string(Bitmap *dst, Font *font, String word, Vec2 pos, B32 draw) {
} }
else { else {
FontGlyph* g = &font->glyphs[word.str[i] - '!']; FontGlyph* g = &font->glyphs[word.str[i] - '!'];
if(draw) r_draw_bitmap(dst, &g->bitmap, pos - g->bitmap.align); if(draw) draw_bitmap(dst, &g->bitmap, pos - g->bitmap.align);
pos.x += g->xadvance; pos.x += g->xadvance;
if (pos.x > max_x) max_x = pos.x; if (pos.x > max_x) max_x = pos.x;
} }
@@ -272,13 +276,13 @@ Vec4 r_base_string(Bitmap *dst, Font *font, String word, Vec2 pos, B32 draw) {
} }
function function
Vec4 r_draw_string(Bitmap *dst, Font *font, String word, Vec2 pos) { Vec4 draw_string(Bitmap *dst, Font *font, String word, Vec2 pos) {
return r_base_string(dst, font, word, pos, true); return base_string(dst, font, word, pos, true);
} }
function function
Vec4 r_get_string_rect(Font *font, String word, Vec2 pos) { Vec4 get_string_rect(Font *font, String word, Vec2 pos) {
return r_base_string(0, font, word, pos, false); return base_string(0, font, word, pos, false);
} }
function function
@@ -493,7 +497,7 @@ void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 lig
} }
function function
void r_scatter_plot(Bitmap *dst, F64 *data, S64 data_len) { void scatter_plot(Bitmap *dst, F64 *data, S64 data_len) {
F64 min = F32MAX; F64 min = F32MAX;
F64 max = F32MIN; F64 max = F32MIN;
F64 step = dst->x / (F64)data_len; F64 step = dst->x / (F64)data_len;
@@ -508,13 +512,13 @@ void r_scatter_plot(Bitmap *dst, F64 *data, S64 data_len) {
*p /= diff; *p /= diff;
F64 y = *p * dst->y; F64 y = *p * dst->y;
x += step; x += step;
r_draw_rect(dst, (F32)x-2, (F32)y-2, 4, 4, vec4(1,0,0,1)); draw_rect(dst, (F32)x-2, (F32)y-2, 4, 4, vec4(1,0,0,1));
//dst->pixels[xi + yi * dst->x] = 0xffff0000; //dst->pixels[xi + yi * dst->x] = 0xffff0000;
} }
} }
function function
void r_draw_mesh(R_Render *r, String scene_name, Obj_Material *materials, Obj_Mesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) { void draw_mesh(Render *r, String scene_name, Obj_Material *materials, Obj_Mesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) {
for (int i = 0; i < mesh->indices.len; i++) { for (int i = 0; i < mesh->indices.len; i++) {
Obj_Index *index = mesh->indices.data + i; Obj_Index *index = mesh->indices.data + i;
Bitmap *image = &r->img; Bitmap *image = &r->img;
@@ -527,7 +531,7 @@ void r_draw_mesh(R_Render *r, String scene_name, Obj_Material *materials, Obj_Me
} }
} }
R_Vertex vert[] = { Vertex vert[] = {
{ {
vertices[index->vertex[0] - 1], vertices[index->vertex[0] - 1],
tex_coords[index->tex[0] - 1], tex_coords[index->tex[0] - 1],
@@ -599,15 +603,15 @@ void r_draw_mesh(R_Render *r, String scene_name, Obj_Material *materials, Obj_Me
Vec3 znear_normal = vec3(0, 0, 1); Vec3 znear_normal = vec3(0, 0, 1);
Vec3 znear_pos = vec3(0, 0, 1.f); Vec3 znear_pos = vec3(0, 0, 1.f);
struct _R_Vertex { struct _Vertex {
Vec4 pos; Vec4 pos;
Vec2 tex; Vec2 tex;
Vec3 norm; Vec3 norm;
} in[4]; } in[4];
S32 in_count = 0; S32 in_count = 0;
R_Vertex *prev = vert + 2; Vertex *prev = vert + 2;
R_Vertex *curr = vert; Vertex *curr = vert;
F32 prev_dot = dot(znear_normal, prev->pos - znear_pos); F32 prev_dot = dot(znear_normal, prev->pos - znear_pos);
F32 curr_dot = 0; F32 curr_dot = 0;
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
@@ -669,7 +673,7 @@ global F32 rotation = 0;
global Obj f22; global Obj f22;
global Obj *sponza; global Obj *sponza;
global Obj *obj; global Obj *obj;
global R_Render r = {}; global Render r = {};
global Scene scene = Scene_Sponza; global Scene scene = Scene_Sponza;
function function
@@ -691,7 +695,13 @@ UI_SIGNAL_CALLBACK(scene_callback) {
scene = (Scene)(((int)scene + 1) % Scene_Count); scene = (Scene)(((int)scene + 1) % Scene_Count);
} }
function void
windows_log(Log_Kind kind, String string, char *file, int line){
OutputDebugStringA((char *)string.str);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
thread_ctx.log_proc = windows_log;
os.window_size.x = 320*2; os.window_size.x = 320*2;
os.window_size.y = 180*2; os.window_size.y = 180*2;
os.window_resizable = 1; os.window_resizable = 1;
@@ -703,7 +713,6 @@ int main(int argc, char **argv) {
//sponza = &sponza_obj; //sponza = &sponza_obj;
//dump_obj_to_file(sponza); //dump_obj_to_file(sponza);
sponza = load_obj_dump(os.perm_arena, "sponza.bin"_s); sponza = load_obj_dump(os.perm_arena, "sponza.bin"_s);
scene_callback(); scene_callback();
int screen_x = 1280/2; int screen_x = 1280/2;
@@ -715,24 +724,6 @@ int main(int argc, char **argv) {
r.depth320 = (F32 *)arena_push_size(os.perm_arena, sizeof(F32) * screen_x * screen_y); r.depth320 = (F32 *)arena_push_size(os.perm_arena, sizeof(F32) * screen_x * screen_y);
r.img = load_image("assets/bricksx64.png"_s); r.img = load_image("assets/bricksx64.png"_s);
/* @Note: Transparent texture */ {
#if 0
Vec4 testc = vec4(1, 1, 1, 0.5f);
testc.rgb *= testc.a;
U32 testc32 = vec4_to_u32abgr(testc);
U32 a[] = { d
testc32, testc32, testc32, testc32,
testc32, testc32, testc32, testc32,
testc32, testc32, testc32, testc32,
testc32, testc32, testc32, testc32,
};
r.img.pixels = a;
r.img.x = 4;
r.img.y = 4;
#endif
}
String frame_data = {}; String frame_data = {};
UISetup setup[] = { UISetup setup[] = {
@@ -795,7 +786,7 @@ int main(int argc, char **argv) {
Vec3 *normals = (Vec3 *)obj->normals.data; Vec3 *normals = (Vec3 *)obj->normals.data;
Obj_Mesh *mesh = obj->mesh.data; Obj_Mesh *mesh = obj->mesh.data;
Vec3* vertices = (Vec3 *)obj->vertices.data; Vec3* vertices = (Vec3 *)obj->vertices.data;
r_draw_mesh(&r, obj->name, obj->materials.data, mesh+i, vertices, tex_coords, normals); draw_mesh(&r, obj->name, obj->materials.data, mesh+i, vertices, tex_coords, normals);
} }

1712
math.h

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,7 @@
///
/// [ ] - Cache bitmaps
/// [ ] - Fix potential portability issues due to compiler struct alignment differences etc.
///
struct Obj_Index { struct Obj_Index {
int vertex[3]; int vertex[3];
@@ -159,9 +163,11 @@ parse_mtl(Obj* obj, String path_obj_folder, String mtl_file) {
m->name_len = clamp_top(token.len, 64); m->name_len = clamp_top(token.len, 64);
memory_copy(m->name, token.s8.str, m->name_len); memory_copy(m->name, token.s8.str, m->name_len);
} }
else if (string_compare(token.s8, "Ns"_s)) { else if (string_compare(token.s8, "Ns"_s)) {
m->shininess = expect_number(&data); m->shininess = expect_number(&data);
} }
else if (string_compare(token.s8, "Ka"_s)) { else if (string_compare(token.s8, "Ka"_s)) {
m->ambient_color.x = expect_number(&data); m->ambient_color.x = expect_number(&data);
m->ambient_color.y = expect_number(&data); m->ambient_color.y = expect_number(&data);
@@ -172,6 +178,7 @@ parse_mtl(Obj* obj, String path_obj_folder, String mtl_file) {
m->diffuse_color.y = expect_number(&data); m->diffuse_color.y = expect_number(&data);
m->diffuse_color.z = expect_number(&data); m->diffuse_color.z = expect_number(&data);
} }
else if (string_compare(token.s8, "Ks"_s)) { else if (string_compare(token.s8, "Ks"_s)) {
m->specular_color.x = expect_number(&data); m->specular_color.x = expect_number(&data);
m->specular_color.y = expect_number(&data); m->specular_color.y = expect_number(&data);
@@ -180,27 +187,32 @@ parse_mtl(Obj* obj, String path_obj_folder, String mtl_file) {
else if (string_compare(token.s8, "Ni"_s)) { else if (string_compare(token.s8, "Ni"_s)) {
m->optical_density = expect_number(&data); m->optical_density = expect_number(&data);
} }
else if (string_compare(token.s8, "d"_s)) { else if (string_compare(token.s8, "d"_s)) {
m->non_transparency = expect_number(&data); m->non_transparency = expect_number(&data);
} }
else if (string_compare(token.s8, "illum"_s)) { else if (string_compare(token.s8, "illum"_s)) {
m->illumination_model = (S32)expect_number(&data); m->illumination_model = (S32)expect_number(&data);
} }
else if (string_compare(token.s8, "map_Kd"_s)) { else if (string_compare(token.s8, "map_Kd"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8); String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
m->texture_diffuse = load_image(path); m->texture_diffuse = load_image(path);
} }
else if (string_compare(token.s8, "map_Ka"_s)) { else if (string_compare(token.s8, "map_Ka"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8); String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
m->texture_ambient = load_image(path); m->texture_ambient = load_image(path);
} }
else if (string_compare(token.s8, "map_d"_s)) { else if (string_compare(token.s8, "map_d"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8); String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
m->texture_dissolve = load_image(path); m->texture_dissolve = load_image(path);
} }
else if (string_compare(token.s8, "map_Disp"_s)) { else if (string_compare(token.s8, "map_Disp"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8); String path = string_fmt(scratch, "%Q/%Q\0", path_obj_folder, t.s8);
@@ -212,17 +224,18 @@ parse_mtl(Obj* obj, String path_obj_folder, String mtl_file) {
function Obj function Obj
parse(Allocator *allocator, char* data, String path_obj_folder) { parse(Allocator *allocator, char* data, String path_obj_folder) {
Set_Allocator(allocator);
Scratch mtl_scratch; Scratch mtl_scratch;
Obj result = {}; Obj result = {};
result.vertices.init(allocator, 160000); //result.vertices.init(allocator, 160000);
result.texture_coordinates.init(allocator, 160000); //result.texture_coordinates.init(allocator, 160000);
result.normals.init(allocator, 160000); //result.normals.init(allocator, 160000);
result.mesh.init(allocator, 64); //result.mesh.init(allocator, 64);
result.materials.init(allocator, 64); //result.materials.init(allocator, 64);
int smoothing = 0; int smoothing = 0;
Obj_Mesh *mesh = result.mesh.push_empty_zero(); Obj_Mesh *mesh = result.mesh.push_empty_zero();
mesh->indices.init(allocator); //mesh->indices.init(allocator);
int material_id = -1; int material_id = -1;
S64 debug_i = 0; S64 debug_i = 0;
@@ -237,12 +250,14 @@ parse(Allocator *allocator, char* data, String path_obj_folder) {
vertex->z = (float)expect_number(&data); vertex->z = (float)expect_number(&data);
debug_expect_raw(&data, Obj_Token_Type::whitespace); debug_expect_raw(&data, Obj_Token_Type::whitespace);
} }
else if (string_compare(token.s8, "vt"_s)) { else if (string_compare(token.s8, "vt"_s)) {
Vec2 *tex = result.texture_coordinates.push_empty_zero(); Vec2 *tex = result.texture_coordinates.push_empty_zero();
tex->x = (float)expect_number(&data); tex->x = (float)expect_number(&data);
tex->y = (float)expect_number(&data); tex->y = (float)expect_number(&data);
debug_expect_raw(&data, Obj_Token_Type::whitespace); debug_expect_raw(&data, Obj_Token_Type::whitespace);
} }
else if (string_compare(token.s8, "vn"_s)) { else if (string_compare(token.s8, "vn"_s)) {
Vec3 *norm = result.normals.push_empty_zero(); Vec3 *norm = result.normals.push_empty_zero();
norm->x = (float)expect_number(&data); norm->x = (float)expect_number(&data);
@@ -250,6 +265,7 @@ parse(Allocator *allocator, char* data, String path_obj_folder) {
norm->z = (float)expect_number(&data); norm->z = (float)expect_number(&data);
debug_expect_raw(&data, Obj_Token_Type::whitespace); debug_expect_raw(&data, Obj_Token_Type::whitespace);
} }
else if (string_compare(token.s8, "mtllib"_s)) { else if (string_compare(token.s8, "mtllib"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
String path = string_fmt(mtl_scratch, "%Q/%Q", path_obj_folder, t.s8); String path = string_fmt(mtl_scratch, "%Q/%Q", path_obj_folder, t.s8);
@@ -258,6 +274,7 @@ parse(Allocator *allocator, char* data, String path_obj_folder) {
parse_mtl(&result, path_obj_folder, mtl_file); parse_mtl(&result, path_obj_folder, mtl_file);
} }
} }
else if (string_compare(token.s8, "usemtl"_s)) { else if (string_compare(token.s8, "usemtl"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
assert(t.type == Obj_Token_Type::word); assert(t.type == Obj_Token_Type::word);
@@ -269,18 +286,19 @@ parse(Allocator *allocator, char* data, String path_obj_folder) {
} }
} }
} }
else if (string_compare(token.s8, "o"_s)) { else if (string_compare(token.s8, "o"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
assert(t.type == Obj_Token_Type::word); assert(t.type == Obj_Token_Type::word);
if (mesh->indices.len != 0) { if (mesh->indices.len != 0) {
mesh = result.mesh.push_empty_zero(); mesh = result.mesh.push_empty_zero();
mesh->indices.init(allocator);
} }
else { else {
U64 len = clamp_top(t.len, 64); U64 len = clamp_top(t.len, 64);
memory_copy(mesh->name, t.s, len); memory_copy(mesh->name, t.s, len);
} }
} }
else if (string_compare(token.s8, "s"_s)) { else if (string_compare(token.s8, "s"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
if (t.type == Obj_Token_Type::number) { if (t.type == Obj_Token_Type::number) {
@@ -298,10 +316,12 @@ parse(Allocator *allocator, char* data, String path_obj_folder) {
} }
} }
else if (string_compare(token.s8, "g"_s)) { else if (string_compare(token.s8, "g"_s)) {
Obj_Token t = next_token(&data); Obj_Token t = next_token(&data);
assert(t.type == Obj_Token_Type::word); assert(t.type == Obj_Token_Type::word);
} }
else if (string_compare(token.s8, "f"_s)) { else if (string_compare(token.s8, "f"_s)) {
Obj_Index *i = mesh->indices.push_empty_zero(); Obj_Index *i = mesh->indices.push_empty_zero();
i->smoothing_group_id = smoothing; i->smoothing_group_id = smoothing;
@@ -330,29 +350,6 @@ parse(Allocator *allocator, char* data, String path_obj_folder) {
return result; return result;
} }
function void
test_lex() {
const char* d = "v 0.885739 0.001910 -0.380334";
char* dd = (char *)d;
assert(next_token(&dd).type == Obj_Token_Type::word);
Obj_Token t = next_token(&dd); assert(t.type == Obj_Token_Type::number && t.number > 0.8857);
t = next_token(&dd); assert(t.type == Obj_Token_Type::number && t.number > 0.0019);
t = next_token(&dd); assert(t.type == Obj_Token_Type::number && t.number < -0.38);
d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n"
"# www.blender.org\n"
"mtllib f-22.mtl\n"
"o F-22\n";
dd = (char *)d;
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "mtllib"_s));
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "f-22.mtl"_s));
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "o"_s));
t = next_token(&dd); assert(t.type == Obj_Token_Type::word && string_compare(t.s8, "F-22"_s));
}
void test() {
test_lex();
}
function Obj function Obj
load_obj(Allocator *arena, String file) { load_obj(Allocator *arena, String file) {
Scratch scratch; Scratch scratch;
@@ -386,25 +383,17 @@ _os_write_file(String file, String data, B32 append = false) {
creation_disposition = OPEN_ALWAYS; creation_disposition = OPEN_ALWAYS;
} }
Scratch scratch; // @Todo(Krzosa): Unicode // @Todo(Krzosa): Unicode
HANDLE handle = CreateFileA((const char *)file.str, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); HANDLE handle = CreateFileA((const char *)file.str, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE) {
DWORD bytes_written = 0; DWORD bytes_written = 0;
// @Todo: can only read 32 byte size files? // @Todo: can only read 32 byte size files?
assert_msg(data.len == (U32)data.len, assert_msg(data.len == (U32)data.len, "Max data size os_write can handle is 32 bytes, data to write is larger then 32 bytes!");
"Max data size os_write can handle is 32 bytes, data to write is "
"larger then 32 bytes!");
B32 error = WriteFile(handle, data.str, (U32)data.len, &bytes_written, NULL); B32 error = WriteFile(handle, data.str, (U32)data.len, &bytes_written, NULL);
if (error == false) { if (error == false) log_error("Failed to write to file: %Q", file);
log_error("Failed to write to file: %Q", file);
}
else { else {
if (bytes_written != data.len) { if (bytes_written != data.len) log_error("Failed to write to file: %Q, mismatch between length requested to write and length written", file);
log_error("Failed to write to file: %Q, mismatch between length requested to write and length written", file); else result = true;
}
else{
result = true;
}
} }
CloseHandle(handle); CloseHandle(handle);
} }
@@ -483,7 +472,7 @@ dump_obj_to_file(Obj *obj){
dump_bitmap_image(&sb, &it->texture_displacement); dump_bitmap_image(&sb, &it->texture_displacement);
} }
String result = string_flatten(&sb); String result = string_flatten(arena, &sb);
os_write_file("sponza.bin"_s, result); os_write_file("sponza.bin"_s, result);
} }

32
ui.cpp
View File

@@ -62,7 +62,7 @@ function UIWidget *ui_new_widget(Allocator *arena, UIWidgetKind kind) {
} }
function void ui_push_child(UIWidget *widget, UIWidget *child) { function void ui_push_child(UIWidget *widget, UIWidget *child) {
//DLL_QUEUE_PUSH(widget->first_child, widget->last_child, child); DLLQueuePush(widget->first_child, widget->last_child, child);
} }
function UIWidget *ui_push_child(Arena *arena, UIWidget *widget, UIWidgetKind kind) { function UIWidget *ui_push_child(Arena *arena, UIWidget *widget, UIWidgetKind kind) {
@@ -171,42 +171,42 @@ function void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
rect = vec4(pos, w->size); rect = vec4(pos, w->size);
ui_mouse_test(ui, w, rect); ui_mouse_test(ui, w, rect);
String string = string_fmt(scratch, "%d %d", w->ptr.image->x, w->ptr.image->y); String string = string_fmt(scratch, "%d %d", w->ptr.image->x, w->ptr.image->y);
r_draw_string(dst, font, string, pos); draw_string(dst, font, string, pos);
r_draw_bitmap(dst, w->ptr.image, pos, w->size); draw_bitmap(dst, w->ptr.image, pos, w->size);
if (ui->active == w) { if (ui->active == w) {
F32 ratio = (F32)w->ptr.image->y / (F32)w->ptr.image->x; F32 ratio = (F32)w->ptr.image->y / (F32)w->ptr.image->x;
w->size.x -= os.delta_mouse_pos.x; w->size.x -= os.delta_mouse_pos.x;
w->size.y = w->size.x * ratio; w->size.y = w->size.x * ratio;
} }
if (ui->hot == w) { if (ui->hot == w) {
r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, vec4(1, 1, 1, 0.1f)); draw_rect(dst, rect.x, rect.y, rect.width, rect.height, vec4(1, 1, 1, 0.1f));
} }
} break; } break;
case UIWidgetKind_Boolean: { case UIWidgetKind_Boolean: {
pos.y -= font->height; pos.y -= font->height;
Vec4 color = vec4(0, 0, 0, 1); Vec4 color = vec4(0, 0, 0, 1);
String string = string_fmt(scratch, "%s %d", w->text, *w->ptr.b32); String string = string_fmt(scratch, "%s %d", w->text, *w->ptr.b32);
rect = r_get_string_rect(font, string, pos); rect = get_string_rect(font, string, pos);
B32 clicked = ui_mouse_test(ui, w, rect); B32 clicked = ui_mouse_test(ui, w, rect);
if (clicked) *w->ptr.b32 = !*w->ptr.b32; if (clicked) *w->ptr.b32 = !*w->ptr.b32;
if (ui->hot == w) { if (ui->hot == w) {
color = vec4(0.4f, 0.4f, 0.4f, 1.f); color = vec4(0.4f, 0.4f, 0.4f, 1.f);
} }
rect.y = rect.y-font->line_advance / 5; rect.y = rect.y-font->line_advance / 5;
r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color); draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color);
rect = r_draw_string(dst, font, string, pos); rect = draw_string(dst, font, string, pos);
pos.y -= rect.height - font->height; pos.y -= rect.height - font->height;
} break; } break;
case UIWidgetKind_Label: { case UIWidgetKind_Label: {
pos.y -= font->height; pos.y -= font->height;
rect = r_draw_string(dst, font, *w->ptr.label, pos); rect = draw_string(dst, font, *w->ptr.label, pos);
pos.y -= rect.height - font->height; pos.y -= rect.height - font->height;
} break; } break;
case UIWidgetKind_Option: { case UIWidgetKind_Option: {
pos.y -= font->height; pos.y -= font->height;
Vec4 color = vec4(0, 0, 0, 1); Vec4 color = vec4(0, 0, 0, 1);
String string = string_fmt(scratch, "%s %d", w->text, *w->ptr.option); String string = string_fmt(scratch, "%Q %Q", w->text, *w->ptr.option);
rect = r_get_string_rect(font, string, pos); rect = get_string_rect(font, string, pos);
B32 clicked = ui_mouse_test(ui, w, rect); B32 clicked = ui_mouse_test(ui, w, rect);
if (clicked) { if (clicked) {
*w->ptr.b32 = (*w->ptr.b32+1) % w->option_max; *w->ptr.b32 = (*w->ptr.b32+1) % w->option_max;
@@ -215,15 +215,15 @@ function void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
color = vec4(0.4f, 0.4f, 0.4f, 1.f); color = vec4(0.4f, 0.4f, 0.4f, 1.f);
} }
rect.y = rect.y-font->line_advance / 5; rect.y = rect.y-font->line_advance / 5;
r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color); draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color);
rect = r_draw_string(dst, font, string, pos); rect = draw_string(dst, font, string, pos);
pos.y -= rect.height - font->height; pos.y -= rect.height - font->height;
} break; } break;
case UIWidgetKind_Signal: { case UIWidgetKind_Signal: {
pos.y -= font->height; pos.y -= font->height;
Vec4 color = vec4(0, 0, 0, 1); Vec4 color = vec4(0, 0, 0, 1);
String string = string_fmt(scratch, "%s", w->text); String string = string_fmt(scratch, "%Q", w->text);
rect = r_get_string_rect(font, string, pos); rect = get_string_rect(font, string, pos);
B32 clicked = ui_mouse_test(ui, w, rect); B32 clicked = ui_mouse_test(ui, w, rect);
if (clicked) { if (clicked) {
w->ptr.signal_callback(); w->ptr.signal_callback();
@@ -232,8 +232,8 @@ function void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
color = vec4(0.4f, 0.4f, 0.4f, 1.f); color = vec4(0.4f, 0.4f, 0.4f, 1.f);
} }
rect.y = rect.y-font->line_advance / 5; rect.y = rect.y-font->line_advance / 5;
r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color); draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color);
rect = r_draw_string(dst, font, string, pos); rect = draw_string(dst, font, string, pos);
pos.y -= rect.height - font->height; pos.y -= rect.height - font->height;
} break; } break;
invalid_default_case; invalid_default_case;