This commit is contained in:
Krzosa Karol
2022-03-03 16:55:58 +01:00
parent af822c4dbe
commit 1d853fc4e3
3 changed files with 345 additions and 306 deletions

View File

@@ -38,9 +38,15 @@
/// - [x] Maybe should clip a triangle on znear zfar plane? /// - [x] Maybe should clip a triangle on znear zfar plane?
/// - [x] Maybe should clip out triangles that are fully z out before draw_triangle /// - [x] Maybe should clip out triangles that are fully z out before draw_triangle
/// - [ ] Effects!!! /// - [ ] Effects!!!
/// - [ ] Outlines
/// - [ ] Lightning /// - [ ] Lightning
/// - [x] GLOBAL Ilumination /// - [ ] Proper normal interpolation
/// * https://hero.handmade.network/episode/code/day101/#105
/// - [ ] Phong /// - [ ] Phong
/// - [x] diffuse
/// - [x] ambient
/// - [ ] specular
/// * reflecting vectors
/// - [ ] Use all materials from OBJ /// - [ ] Use all materials from OBJ
/// - [ ] Point light /// - [ ] Point light
/// - [ ] Reading PMX files /// - [ ] Reading PMX files
@@ -62,6 +68,7 @@
/// - [ ] UI /// - [ ] UI
/// - [x] Labels /// - [x] Labels
/// - [x] Settings variables /// - [x] Settings variables
/// - [x] Signals
/// - [ ] Sliders /// - [ ] Sliders
/// - [ ] Groups /// - [ ] Groups
/// - [x] Gamma correct alpha blending for rectangles and bitmaps /// - [x] Gamma correct alpha blending for rectangles and bitmaps
@@ -113,10 +120,10 @@ enum Scene {
Scene_Count, Scene_Count,
}; };
GLOBAL B32 draw_rects = 0;
GLOBAL Scene scene = Scene_Sponza;
GLOBAL F32 zfar_value = 100000.f;
GLOBAL F32 light_rotation = 0; GLOBAL F32 light_rotation = 0;
GLOBAL F32 zfar_value = 100000.f;
FUNCTION FUNCTION
Vec4 srgb_to_almost_linear(Vec4 a) { Vec4 srgb_to_almost_linear(Vec4 a) {
@@ -380,15 +387,16 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig
dst_color = { r,g,b,a }; dst_color = { r,g,b,a };
} }
F32 light = -dot(norm, light_direction); Vec3 light_color = vec3(0.8,0.8,1);
{ constexpr F32 ambient_strength = 0.1f; {
light = CLAMP(0.1f, light, 1.f); Vec3 ambient = ambient_strength * light_color;
result_color.r *= light; Vec3 diffuse = CLAMP_BOT(0, -dot(norm, light_direction)) * light_color;
result_color.g *= light; result_color.rgb *= (ambient+diffuse);
result_color.b *= light;
} }
result_color = premultiplied_alpha(dst_color, result_color); result_color = premultiplied_alpha(dst_color, result_color);
result_color = almost_linear_to_srgb(result_color); result_color = almost_linear_to_srgb(result_color);
U32 color32 = vec4_to_u32abgr(result_color); U32 color32 = vec4_to_u32abgr(result_color);
@@ -408,11 +416,7 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig
Cy2 -= dx02; Cy2 -= dx02;
destination += dst->x; destination += dst->x;
} }
if (draw_rects) {
r_draw_rect(dst, p0.x-4, p0.y-4, 8,8, vec4(1,0,0,1));
r_draw_rect(dst, p1.x-4, p1.y-4, 8,8, vec4(0,1,0,1));
r_draw_rect(dst, p2.x-4, p2.y-4, 8,8, vec4(0,0,1,1));
}
if(os.frame > 60) PROFILE_END(draw_triangle); if(os.frame > 60) PROFILE_END(draw_triangle);
} }
@@ -489,11 +493,6 @@ void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 lig
} }
} }
} }
if (draw_rects) {
r_draw_rect(dst, p0.x-4, p0.y-4, 8,8, vec4(1,0,0,1));
r_draw_rect(dst, p1.x-4, p1.y-4, 8,8, vec4(0,1,0,1));
r_draw_rect(dst, p2.x-4, p2.y-4, 8,8, vec4(0,0,1,1));
}
} }
FUNCTION FUNCTION
@@ -668,6 +667,31 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
} }
#include "ui.cpp" #include "ui.cpp"
F32 speed = 100.f;
F32 rotation = 0;
Obj f22;
Obj sponza;
Obj *obj;
R_Render r = {};
GLOBAL Scene scene = Scene_Sponza;
UI_SIGNAL_CALLBACK(scene_callback) {
switch(scene) {
case Scene_F22: {
speed = 1;
r.camera_pos = vec3(0,0,-2);
obj = &f22;
} break;
case Scene_Sponza: {
speed = 100;
r.camera_pos = vec3(0,0,-2);
obj = &sponza;
} break;
case Scene_Count:
INVALID_DEFAULT_CASE;
}
scene = (Scene)(((int)scene + 1) % Scene_Count);
}
int main() { int main() {
os.window_size.x = 320*2; os.window_size.x = 320*2;
os.window_size.y = 180*2; os.window_size.y = 180*2;
@@ -691,16 +715,13 @@ int main() {
} }
Obj f22 = load_obj(os.perm_arena, LIT("assets/f22.obj")); f22 = load_obj(os.perm_arena, LIT("assets/f22.obj"));
Obj sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj")); sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj"));
Obj *obj = 0; scene_callback();
F32 speed = 100.f;
F32 rotation = 0;
int screen_x = 1280/2; int screen_x = 1280/2;
int screen_y = 720/2; int screen_y = 720/2;
R_Render r = {};
r.camera_pos = {0,0,-2}; r.camera_pos = {0,0,-2};
r.screen320 = {(U32 *)PUSH_SIZE(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y}; r.screen320 = {(U32 *)PUSH_SIZE(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y};
r.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720}; r.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720};
@@ -728,8 +749,7 @@ int main() {
S8 frame_data = {}; S8 frame_data = {};
UISetup setup[] = { UISetup setup[] = {
UI_BOOL(LIT("Draw rectangles:"), &draw_rects), UI_SIGNAL(LIT("Change scene"), scene_callback),
UI_OPTION(LIT("Scene:"), &scene, Scene_Count),
UI_IMAGE(&r.plot), UI_IMAGE(&r.plot),
UI_LABEL(&frame_data), UI_LABEL(&frame_data),
}; };
@@ -737,18 +757,7 @@ int main() {
B32 ui_mouse_lock = true; B32 ui_mouse_lock = true;
while (os_game_loop()) { while (os_game_loop()) {
switch(scene) {
case Scene_F22: {
speed = 1;
obj = &f22;
} break;
case Scene_Sponza: {
speed = 100;
obj = &sponza;
} break;
case Scene_Count:
INVALID_DEFAULT_CASE;
}
if (ui_mouse_lock == false) { if (ui_mouse_lock == false) {
r.camera_yaw.x += os.delta_mouse_pos.x * 0.01f; r.camera_yaw.x += os.delta_mouse_pos.x * 0.01f;
@@ -757,7 +766,6 @@ int main() {
if (os.key[Key_Escape].pressed) os_quit(); if (os.key[Key_Escape].pressed) os_quit();
if (os.key[Key_O].down) light_rotation += 0.05f; if (os.key[Key_O].down) light_rotation += 0.05f;
if (os.key[Key_P].down) light_rotation -= 0.05f; if (os.key[Key_P].down) light_rotation -= 0.05f;
if (os.key[Key_F1].pressed) draw_rects = !draw_rects;
if (os.key[Key_F2].pressed) { if (os.key[Key_F2].pressed) {
ui_mouse_lock = !ui_mouse_lock; ui_mouse_lock = !ui_mouse_lock;
os_show_cursor(!os.cursor_visible); os_show_cursor(!os.cursor_visible);

View File

@@ -86,13 +86,12 @@ Bitmap load_image(const char* path) {
return result; return result;
} }
namespace obj { enum class OBJTokenType {
enum class TokenType {
none, word, number, whitespace, end none, word, number, whitespace, end
}; };
struct Token { struct OBJToken {
TokenType type; OBJTokenType type;
double number; double number;
union { union {
struct { struct {
@@ -103,20 +102,20 @@ namespace obj {
}; };
}; };
FUNCTION Token next_token_raw(char** data) { FUNCTION OBJToken next_token_raw(char** data) {
Token result = {}; OBJToken result = {};
result.s = *data; result.s = *data;
*data += 1; *data += 1;
if (is_alphabetic(*result.s)) { if (is_alphabetic(*result.s)) {
result.type = TokenType::word; result.type = OBJTokenType::word;
while (!is_whitespace(**data)) { while (!is_whitespace(**data)) {
*data += 1; *data += 1;
} }
result.len = (int)(*data - result.s); result.len = (int)(*data - result.s);
} }
else if (is_number(*result.s) || *result.s == '-') { else if (is_number(*result.s) || *result.s == '-') {
result.type = TokenType::number; result.type = OBJTokenType::number;
while (is_number(**data) || **data == '.' || **data == 'e' || **data == '-') { while (is_number(**data) || **data == '.' || **data == 'e' || **data == '-') {
*data += 1; *data += 1;
} }
@@ -128,42 +127,42 @@ namespace obj {
result = next_token_raw(data); result = next_token_raw(data);
} }
else if (is_whitespace(*result.s)) { else if (is_whitespace(*result.s)) {
result.type = TokenType::whitespace; result.type = OBJTokenType::whitespace;
while (is_whitespace(**data)) *data += 1; while (is_whitespace(**data)) *data += 1;
result.len = (int)(*data - result.s); result.len = (int)(*data - result.s);
} }
else if (*result.s == 0) { else if (*result.s == 0) {
result.type = TokenType::end; result.type = OBJTokenType::end;
} }
else if (*result.s >= '!') { else if (*result.s >= '!') {
result.type = (TokenType)*result.s; result.type = (OBJTokenType)*result.s;
} }
return result; return result;
} }
FUNCTION Token next_token(char** data) { FUNCTION OBJToken next_token(char** data) {
Token result; OBJToken result;
do { do {
result = next_token_raw(data); result = next_token_raw(data);
} while (result.type == TokenType::whitespace); } while (result.type == OBJTokenType::whitespace);
return result; return result;
} }
FUNCTION double expect_number(char** data) { FUNCTION double expect_number(char** data) {
Token t = next_token(data); OBJToken t = next_token(data);
TASSERT(t.type == TokenType::number); // @Todo: Error handling, error flag TASSERT(t.type == OBJTokenType::number); // @Todo: Error handling, error flag
return t.number; return t.number;
} }
FUNCTION void expect_token(char** data, char token) { FUNCTION void expect_token(char** data, char token) {
Token t = next_token(data); OBJToken t = next_token(data);
TASSERT(t.type == (TokenType)token); // @Todo: Error handling, error flag TASSERT(t.type == (OBJTokenType)token); // @Todo: Error handling, error flag
} }
FUNCTION void debug_expect_raw(char** data, TokenType type) { FUNCTION void debug_expect_raw(char** data, OBJTokenType type) {
char* data_temp = *data; char* data_temp = *data;
Token t = next_token_raw(&data_temp); OBJToken t = next_token_raw(&data_temp);
TASSERT(t.type == type); TASSERT(t.type == type);
} }
@@ -172,9 +171,9 @@ namespace obj {
char *data = (char *)mtl_file.str; char *data = (char *)mtl_file.str;
OBJMaterial *m = 0; OBJMaterial *m = 0;
for (;;) { for (;;) {
Token token = next_token(&data); OBJToken token = next_token(&data);
if (token.type == TokenType::end) break; if (token.type == OBJTokenType::end) break;
else if (token.type == TokenType::word) { else if (token.type == OBJTokenType::word) {
if (string_compare(token.s8, LIT("newmtl"))) { if (string_compare(token.s8, LIT("newmtl"))) {
token = next_token(&data); token = next_token(&data);
m = obj->materials.push_empty(); m = obj->materials.push_empty();
@@ -210,22 +209,22 @@ namespace obj {
m->illumination_model = (I32)expect_number(&data); m->illumination_model = (I32)expect_number(&data);
} }
else if (string_compare(token.s8, LIT("map_Kd"))) { else if (string_compare(token.s8, LIT("map_Kd"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
m->texture_diffuse = load_image((const char *)path.str); m->texture_diffuse = load_image((const char *)path.str);
} }
else if (string_compare(token.s8, LIT("map_Ka"))) { else if (string_compare(token.s8, LIT("map_Ka"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
m->texture_ambient = load_image((const char *)path.str); m->texture_ambient = load_image((const char *)path.str);
} }
else if (string_compare(token.s8, LIT("map_d"))) { else if (string_compare(token.s8, LIT("map_d"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
m->texture_dissolve = load_image((const char *)path.str); m->texture_dissolve = load_image((const char *)path.str);
} }
else if (string_compare(token.s8, LIT("map_Disp"))) { else if (string_compare(token.s8, LIT("map_Disp"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8);
m->texture_displacment = load_image((const char *)path.str); m->texture_displacment = load_image((const char *)path.str);
} }
@@ -242,31 +241,31 @@ namespace obj {
int material_id = -1; int material_id = -1;
for (;; ) { for (;; ) {
Token token = next_token(&data); OBJToken token = next_token(&data);
if (token.type == TokenType::end) break; if (token.type == OBJTokenType::end) break;
else if (token.type == TokenType::word) { else if (token.type == OBJTokenType::word) {
if (string_compare(token.s8, LIT("v"))) { if (string_compare(token.s8, LIT("v"))) {
Vec3 *vertex = result.vertices.push_empty(); Vec3 *vertex = result.vertices.push_empty();
vertex->x = (float)expect_number(&data); vertex->x = (float)expect_number(&data);
vertex->y = (float)expect_number(&data); vertex->y = (float)expect_number(&data);
vertex->z = (float)expect_number(&data); vertex->z = (float)expect_number(&data);
debug_expect_raw(&data, TokenType::whitespace); debug_expect_raw(&data, OBJTokenType::whitespace);
} }
else if (string_compare(token.s8, LIT("vt"))) { else if (string_compare(token.s8, LIT("vt"))) {
Vec2 *tex = result.texture_coordinates.push_empty(); Vec2 *tex = result.texture_coordinates.push_empty();
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, TokenType::whitespace); debug_expect_raw(&data, OBJTokenType::whitespace);
} }
else if (string_compare(token.s8, LIT("vn"))) { else if (string_compare(token.s8, LIT("vn"))) {
Vec3 *norm = result.normals.push_empty(); Vec3 *norm = result.normals.push_empty();
norm->x = (float)expect_number(&data); norm->x = (float)expect_number(&data);
norm->y = (float)expect_number(&data); norm->y = (float)expect_number(&data);
norm->z = (float)expect_number(&data); norm->z = (float)expect_number(&data);
debug_expect_raw(&data, TokenType::whitespace); debug_expect_raw(&data, OBJTokenType::whitespace);
} }
else if (string_compare(token.s8, LIT("mtllib"))) { else if (string_compare(token.s8, LIT("mtllib"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8); S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8);
Result<S8> mtl_file = os_read_file(mtl_scratch, path); Result<S8> mtl_file = os_read_file(mtl_scratch, path);
if(mtl_file.no_error()) { if(mtl_file.no_error()) {
@@ -275,8 +274,8 @@ namespace obj {
} }
} }
else if (string_compare(token.s8, LIT("usemtl"))) { else if (string_compare(token.s8, LIT("usemtl"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
TASSERT(t.type == TokenType::word); TASSERT(t.type == OBJTokenType::word);
for(U64 i = 0; i < result.materials.len; i++) { for(U64 i = 0; i < result.materials.len; i++) {
OBJMaterial *m = result.materials.e + i; OBJMaterial *m = result.materials.e + i;
if(string_compare(string_make((U8 *)m->name, m->name_len), t.s8)) { if(string_compare(string_make((U8 *)m->name, m->name_len), t.s8)) {
@@ -286,8 +285,8 @@ namespace obj {
} }
} }
else if (string_compare(token.s8, LIT("o"))) { else if (string_compare(token.s8, LIT("o"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
TASSERT(t.type == TokenType::word); TASSERT(t.type == OBJTokenType::word);
if (mesh->indices.len != 0) { if (mesh->indices.len != 0) {
mesh = result.mesh.push_empty(); mesh = result.mesh.push_empty();
ZERO_STRUCT(mesh); ZERO_STRUCT(mesh);
@@ -298,12 +297,12 @@ namespace obj {
} }
} }
else if (string_compare(token.s8, LIT("s"))) { else if (string_compare(token.s8, LIT("s"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
if (t.type == TokenType::number) { if (t.type == OBJTokenType::number) {
smoothing = (int)t.number; smoothing = (int)t.number;
} }
else { else {
TASSERT(t.type == TokenType::word); TASSERT(t.type == OBJTokenType::word);
if (string_compare(t.s8, LIT("on"))) { if (string_compare(t.s8, LIT("on"))) {
smoothing = 1; smoothing = 1;
} }
@@ -315,8 +314,8 @@ namespace obj {
} }
else if (string_compare(token.s8, LIT("g"))) { else if (string_compare(token.s8, LIT("g"))) {
Token t = next_token(&data); OBJToken t = next_token(&data);
TASSERT(t.type == TokenType::word); TASSERT(t.type == OBJTokenType::word);
} }
else if (string_compare(token.s8, LIT("f"))) { else if (string_compare(token.s8, LIT("f"))) {
ObjIndex *i = mesh->indices.push_empty(); ObjIndex *i = mesh->indices.push_empty();
@@ -339,7 +338,7 @@ namespace obj {
i->tex[2] = (int)expect_number(&data); i->tex[2] = (int)expect_number(&data);
expect_token(&data, '/'); expect_token(&data, '/');
i->normal[2] = (int)expect_number(&data); i->normal[2] = (int)expect_number(&data);
//debug_expect_raw(&data, TokenType::whitespace); //debug_expect_raw(&data, OBJTokenType::whitespace);
} }
} }
} }
@@ -349,32 +348,31 @@ namespace obj {
FUNCTION void test_lex() { FUNCTION void test_lex() {
const char* d = "v 0.885739 0.001910 -0.380334"; const char* d = "v 0.885739 0.001910 -0.380334";
char* dd = (char *)d; char* dd = (char *)d;
TASSERT(next_token(&dd).type == TokenType::word); TASSERT(next_token(&dd).type == OBJTokenType::word);
Token t = next_token(&dd); TASSERT(t.type == TokenType::number && t.number > 0.8857); OBJToken t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.8857);
t = next_token(&dd); TASSERT(t.type == TokenType::number && t.number > 0.0019); t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.0019);
t = next_token(&dd); TASSERT(t.type == TokenType::number && t.number < -0.38); t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number < -0.38);
d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n" d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n"
"# www.blender.org\n" "# www.blender.org\n"
"mtllib f-22.mtl\n" "mtllib f-22.mtl\n"
"o F-22\n"; "o F-22\n";
dd = (char *)d; dd = (char *)d;
t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("mtllib"))); t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("mtllib")));
t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("f-22.mtl"))); t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("f-22.mtl")));
t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("o"))); t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("o")));
t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("F-22"))); t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("F-22")));
} }
void test() { void test() {
test_lex(); test_lex();
} }
}
FUNCTION Obj load_obj(Arena *arena, S8 file) { FUNCTION Obj load_obj(Arena *arena, S8 file) {
Scratch scratch; Scratch scratch;
S8 data = os_read_file(scratch, file).error_is_fatal(); S8 data = os_read_file(scratch, file).error_is_fatal();
PUSH_SIZE(scratch, 1); PUSH_SIZE(scratch, 1);
S8 path = string_chop_last_slash(file); S8 path = string_chop_last_slash(file);
Obj result = obj::parse(arena, (char *)data.str, path); Obj result = parse(arena, (char *)data.str, path);
result.name = file; result.name = file;
return result; return result;
} }

33
ui.cpp
View File

@@ -4,9 +4,12 @@ enum UIWidgetKind {
UIWidgetKind_Image, UIWidgetKind_Image,
UIWidgetKind_Label, UIWidgetKind_Label,
UIWidgetKind_Option, UIWidgetKind_Option,
UIWidgetKind_Signal,
UIWidgetKind_Group, UIWidgetKind_Group,
}; };
#define UI_SIGNAL_CALLBACK(name) void name()
typedef UI_SIGNAL_CALLBACK(UISignalCallback);
struct UISetup { struct UISetup {
UIWidgetKind kind; UIWidgetKind kind;
S8 text; S8 text;
@@ -16,6 +19,7 @@ struct UISetup {
B32 *b32; B32 *b32;
S8 *label; S8 *label;
I32 *option; I32 *option;
UISignalCallback *signal_callback;
}; };
I32 option_max; I32 option_max;
}; };
@@ -23,6 +27,7 @@ struct UISetup {
#define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)} #define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)}
#define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)} #define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)}
#define UI_OPTION(text,x,option_max){UIWidgetKind_Option,text,(void*)(x),(option_max)} #define UI_OPTION(text,x,option_max){UIWidgetKind_Option,text,(void*)(x),(option_max)}
#define UI_SIGNAL(text,x){UIWidgetKind_Signal,text,(void*)(x)}
struct UIWidget { struct UIWidget {
UIWidgetKind kind; UIWidgetKind kind;
@@ -39,6 +44,7 @@ struct UIWidget {
B32 *b32; B32 *b32;
S8 *label; S8 *label;
I32 *option; I32 *option;
UISignalCallback *signal_callback;
} ptr; } ptr;
}; };
@@ -96,6 +102,13 @@ FUNCTION UIWidget *ui_push_option(Arena *arena, UIWidget *widget, S8 string, I32
return result; return result;
} }
FUNCTION UIWidget *ui_push_signal(Arena *arena, UIWidget *widget, S8 string, UISignalCallback *callback) {
UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Signal);
result->text = string;
result->ptr.signal_callback = callback;
return result;
}
FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) { FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
UI result = {}; UI result = {};
result.arena = arena_sub(arena, MiB(16)); result.arena = arena_sub(arena, MiB(16));
@@ -114,6 +127,9 @@ FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
case UIWidgetKind_Option: { case UIWidgetKind_Option: {
ui_push_option(&result.arena, parent, s->text, s->option, s->option_max); ui_push_option(&result.arena, parent, s->text, s->option, s->option_max);
} break; } break;
case UIWidgetKind_Signal: {
ui_push_signal(&result.arena, parent, s->text, s->signal_callback);
} break;
INVALID_DEFAULT_CASE; INVALID_DEFAULT_CASE;
} }
} }
@@ -203,6 +219,23 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) {
rect = r_draw_string(dst, font, string, pos); rect = r_draw_string(dst, font, string, pos);
pos.y -= rect.height - font->height; pos.y -= rect.height - font->height;
} break; } break;
case UIWidgetKind_Signal: {
pos.y -= font->height;
Vec4 color = vec4(0, 0, 0, 1);
S8 string = string_format(scratch, "%s", w->text);
rect = r_get_string_rect(font, string, pos);
B32 clicked = ui_mouse_test(ui, w, rect);
if (clicked) {
w->ptr.signal_callback();
}
if (ui->hot == w) {
color = vec4(0.4f, 0.4f, 0.4f, 1.f);
}
rect.y = rect.y-font->line_advance / 5;
r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color);
rect = r_draw_string(dst, font, string, pos);
pos.y -= rect.height - font->height;
} break;
INVALID_DEFAULT_CASE; INVALID_DEFAULT_CASE;
} }
} }