Signals
This commit is contained in:
92
main.cpp
92
main.cpp
@@ -38,9 +38,15 @@
|
||||
/// - [x] Maybe should clip a triangle on znear zfar plane?
|
||||
/// - [x] Maybe should clip out triangles that are fully z out before draw_triangle
|
||||
/// - [ ] Effects!!!
|
||||
/// - [ ] Outlines
|
||||
/// - [ ] Lightning
|
||||
/// - [x] GLOBAL Ilumination
|
||||
/// - [ ] Proper normal interpolation
|
||||
/// * https://hero.handmade.network/episode/code/day101/#105
|
||||
/// - [ ] Phong
|
||||
/// - [x] diffuse
|
||||
/// - [x] ambient
|
||||
/// - [ ] specular
|
||||
/// * reflecting vectors
|
||||
/// - [ ] Use all materials from OBJ
|
||||
/// - [ ] Point light
|
||||
/// - [ ] Reading PMX files
|
||||
@@ -62,6 +68,7 @@
|
||||
/// - [ ] UI
|
||||
/// - [x] Labels
|
||||
/// - [x] Settings variables
|
||||
/// - [x] Signals
|
||||
/// - [ ] Sliders
|
||||
/// - [ ] Groups
|
||||
/// - [x] Gamma correct alpha blending for rectangles and bitmaps
|
||||
@@ -113,10 +120,10 @@ enum Scene {
|
||||
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 zfar_value = 100000.f;
|
||||
|
||||
|
||||
FUNCTION
|
||||
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 };
|
||||
}
|
||||
|
||||
F32 light = -dot(norm, light_direction);
|
||||
{
|
||||
light = CLAMP(0.1f, light, 1.f);
|
||||
result_color.r *= light;
|
||||
result_color.g *= light;
|
||||
result_color.b *= light;
|
||||
Vec3 light_color = vec3(0.8,0.8,1);
|
||||
constexpr F32 ambient_strength = 0.1f; {
|
||||
Vec3 ambient = ambient_strength * light_color;
|
||||
Vec3 diffuse = CLAMP_BOT(0, -dot(norm, light_direction)) * light_color;
|
||||
result_color.rgb *= (ambient+diffuse);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
result_color = premultiplied_alpha(dst_color, result_color);
|
||||
result_color = almost_linear_to_srgb(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;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -668,6 +667,31 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me
|
||||
}
|
||||
#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() {
|
||||
os.window_size.x = 320*2;
|
||||
os.window_size.y = 180*2;
|
||||
@@ -691,16 +715,13 @@ int main() {
|
||||
}
|
||||
|
||||
|
||||
Obj f22 = load_obj(os.perm_arena, LIT("assets/f22.obj"));
|
||||
Obj sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj"));
|
||||
Obj *obj = 0;
|
||||
|
||||
F32 speed = 100.f;
|
||||
F32 rotation = 0;
|
||||
f22 = load_obj(os.perm_arena, LIT("assets/f22.obj"));
|
||||
sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj"));
|
||||
scene_callback();
|
||||
|
||||
int screen_x = 1280/2;
|
||||
int screen_y = 720/2;
|
||||
R_Render r = {};
|
||||
|
||||
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.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720};
|
||||
@@ -728,8 +749,7 @@ int main() {
|
||||
|
||||
S8 frame_data = {};
|
||||
UISetup setup[] = {
|
||||
UI_BOOL(LIT("Draw rectangles:"), &draw_rects),
|
||||
UI_OPTION(LIT("Scene:"), &scene, Scene_Count),
|
||||
UI_SIGNAL(LIT("Change scene"), scene_callback),
|
||||
UI_IMAGE(&r.plot),
|
||||
UI_LABEL(&frame_data),
|
||||
};
|
||||
@@ -737,18 +757,7 @@ int main() {
|
||||
B32 ui_mouse_lock = true;
|
||||
|
||||
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) {
|
||||
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_O].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) {
|
||||
ui_mouse_lock = !ui_mouse_lock;
|
||||
os_show_cursor(!os.cursor_visible);
|
||||
|
||||
138
obj_parser.cpp
138
obj_parser.cpp
@@ -86,13 +86,12 @@ Bitmap load_image(const char* path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace obj {
|
||||
enum class TokenType {
|
||||
enum class OBJTokenType {
|
||||
none, word, number, whitespace, end
|
||||
};
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
struct OBJToken {
|
||||
OBJTokenType type;
|
||||
double number;
|
||||
union {
|
||||
struct {
|
||||
@@ -101,22 +100,22 @@ namespace obj {
|
||||
};
|
||||
S8 s8;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
FUNCTION Token next_token_raw(char** data) {
|
||||
Token result = {};
|
||||
FUNCTION OBJToken next_token_raw(char** data) {
|
||||
OBJToken result = {};
|
||||
result.s = *data;
|
||||
*data += 1;
|
||||
|
||||
if (is_alphabetic(*result.s)) {
|
||||
result.type = TokenType::word;
|
||||
result.type = OBJTokenType::word;
|
||||
while (!is_whitespace(**data)) {
|
||||
*data += 1;
|
||||
}
|
||||
result.len = (int)(*data - 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 == '-') {
|
||||
*data += 1;
|
||||
}
|
||||
@@ -128,53 +127,53 @@ namespace obj {
|
||||
result = next_token_raw(data);
|
||||
}
|
||||
else if (is_whitespace(*result.s)) {
|
||||
result.type = TokenType::whitespace;
|
||||
result.type = OBJTokenType::whitespace;
|
||||
while (is_whitespace(**data)) *data += 1;
|
||||
result.len = (int)(*data - result.s);
|
||||
}
|
||||
else if (*result.s == 0) {
|
||||
result.type = TokenType::end;
|
||||
result.type = OBJTokenType::end;
|
||||
}
|
||||
else if (*result.s >= '!') {
|
||||
result.type = (TokenType)*result.s;
|
||||
result.type = (OBJTokenType)*result.s;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION Token next_token(char** data) {
|
||||
Token result;
|
||||
FUNCTION OBJToken next_token(char** data) {
|
||||
OBJToken result;
|
||||
do {
|
||||
result = next_token_raw(data);
|
||||
} while (result.type == TokenType::whitespace);
|
||||
} while (result.type == OBJTokenType::whitespace);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION double expect_number(char** data) {
|
||||
Token t = next_token(data);
|
||||
TASSERT(t.type == TokenType::number); // @Todo: Error handling, error flag
|
||||
FUNCTION double expect_number(char** data) {
|
||||
OBJToken t = next_token(data);
|
||||
TASSERT(t.type == OBJTokenType::number); // @Todo: Error handling, error flag
|
||||
return t.number;
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION void expect_token(char** data, char token) {
|
||||
Token t = next_token(data);
|
||||
TASSERT(t.type == (TokenType)token); // @Todo: Error handling, error flag
|
||||
}
|
||||
FUNCTION void expect_token(char** data, char token) {
|
||||
OBJToken t = next_token(data);
|
||||
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;
|
||||
Token t = next_token_raw(&data_temp);
|
||||
OBJToken t = next_token_raw(&data_temp);
|
||||
TASSERT(t.type == type);
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION void parse_mtl(Arena *arena, Obj* obj, S8 path_obj_folder, S8 mtl_file) {
|
||||
FUNCTION void parse_mtl(Arena *arena, Obj* obj, S8 path_obj_folder, S8 mtl_file) {
|
||||
Scratch scratch;
|
||||
char *data = (char *)mtl_file.str;
|
||||
OBJMaterial *m = 0;
|
||||
for (;;) {
|
||||
Token token = next_token(&data);
|
||||
if (token.type == TokenType::end) break;
|
||||
else if (token.type == TokenType::word) {
|
||||
OBJToken token = next_token(&data);
|
||||
if (token.type == OBJTokenType::end) break;
|
||||
else if (token.type == OBJTokenType::word) {
|
||||
if (string_compare(token.s8, LIT("newmtl"))) {
|
||||
token = next_token(&data);
|
||||
m = obj->materials.push_empty();
|
||||
@@ -210,30 +209,30 @@ namespace obj {
|
||||
m->illumination_model = (I32)expect_number(&data);
|
||||
}
|
||||
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);
|
||||
m->texture_diffuse = load_image((const char *)path.str);
|
||||
}
|
||||
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);
|
||||
m->texture_ambient = load_image((const char *)path.str);
|
||||
}
|
||||
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);
|
||||
m->texture_dissolve = load_image((const char *)path.str);
|
||||
}
|
||||
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);
|
||||
m->texture_displacment = load_image((const char *)path.str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION Obj parse(Arena *arena, char* data, S8 path_obj_folder) {
|
||||
FUNCTION Obj parse(Arena *arena, char* data, S8 path_obj_folder) {
|
||||
Scratch mtl_scratch;
|
||||
Obj result = {};
|
||||
int smoothing = 0;
|
||||
@@ -242,31 +241,31 @@ namespace obj {
|
||||
int material_id = -1;
|
||||
|
||||
for (;; ) {
|
||||
Token token = next_token(&data);
|
||||
if (token.type == TokenType::end) break;
|
||||
else if (token.type == TokenType::word) {
|
||||
OBJToken token = next_token(&data);
|
||||
if (token.type == OBJTokenType::end) break;
|
||||
else if (token.type == OBJTokenType::word) {
|
||||
if (string_compare(token.s8, LIT("v"))) {
|
||||
Vec3 *vertex = result.vertices.push_empty();
|
||||
vertex->x = (float)expect_number(&data);
|
||||
vertex->y = (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"))) {
|
||||
Vec2 *tex = result.texture_coordinates.push_empty();
|
||||
tex->x = (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"))) {
|
||||
Vec3 *norm = result.normals.push_empty();
|
||||
norm->x = (float)expect_number(&data);
|
||||
norm->y = (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"))) {
|
||||
Token t = next_token(&data);
|
||||
OBJToken t = next_token(&data);
|
||||
S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8);
|
||||
Result<S8> mtl_file = os_read_file(mtl_scratch, path);
|
||||
if(mtl_file.no_error()) {
|
||||
@@ -275,8 +274,8 @@ namespace obj {
|
||||
}
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("usemtl"))) {
|
||||
Token t = next_token(&data);
|
||||
TASSERT(t.type == TokenType::word);
|
||||
OBJToken t = next_token(&data);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
for(U64 i = 0; i < result.materials.len; i++) {
|
||||
OBJMaterial *m = result.materials.e + i;
|
||||
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"))) {
|
||||
Token t = next_token(&data);
|
||||
TASSERT(t.type == TokenType::word);
|
||||
OBJToken t = next_token(&data);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
if (mesh->indices.len != 0) {
|
||||
mesh = result.mesh.push_empty();
|
||||
ZERO_STRUCT(mesh);
|
||||
@@ -298,12 +297,12 @@ namespace obj {
|
||||
}
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("s"))) {
|
||||
Token t = next_token(&data);
|
||||
if (t.type == TokenType::number) {
|
||||
OBJToken t = next_token(&data);
|
||||
if (t.type == OBJTokenType::number) {
|
||||
smoothing = (int)t.number;
|
||||
}
|
||||
else {
|
||||
TASSERT(t.type == TokenType::word);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
if (string_compare(t.s8, LIT("on"))) {
|
||||
smoothing = 1;
|
||||
}
|
||||
@@ -315,8 +314,8 @@ namespace obj {
|
||||
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("g"))) {
|
||||
Token t = next_token(&data);
|
||||
TASSERT(t.type == TokenType::word);
|
||||
OBJToken t = next_token(&data);
|
||||
TASSERT(t.type == OBJTokenType::word);
|
||||
}
|
||||
else if (string_compare(token.s8, LIT("f"))) {
|
||||
ObjIndex *i = mesh->indices.push_empty();
|
||||
@@ -339,34 +338,33 @@ namespace obj {
|
||||
i->tex[2] = (int)expect_number(&data);
|
||||
expect_token(&data, '/');
|
||||
i->normal[2] = (int)expect_number(&data);
|
||||
//debug_expect_raw(&data, TokenType::whitespace);
|
||||
//debug_expect_raw(&data, OBJTokenType::whitespace);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION void test_lex() {
|
||||
FUNCTION void test_lex() {
|
||||
const char* d = "v 0.885739 0.001910 -0.380334";
|
||||
char* dd = (char *)d;
|
||||
TASSERT(next_token(&dd).type == TokenType::word);
|
||||
Token t = next_token(&dd); TASSERT(t.type == TokenType::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 == TokenType::number && t.number < -0.38);
|
||||
TASSERT(next_token(&dd).type == OBJTokenType::word);
|
||||
OBJToken t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.8857);
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.0019);
|
||||
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"
|
||||
"# www.blender.org\n"
|
||||
"mtllib f-22.mtl\n"
|
||||
"o F-22\n";
|
||||
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 == TokenType::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 == TokenType::word && string_compare(t.s8, LIT("F-22")));
|
||||
}
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("mtllib")));
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("f-22.mtl")));
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("o")));
|
||||
t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("F-22")));
|
||||
}
|
||||
|
||||
void test() {
|
||||
void test() {
|
||||
test_lex();
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION Obj load_obj(Arena *arena, S8 file) {
|
||||
@@ -374,7 +372,7 @@ FUNCTION Obj load_obj(Arena *arena, S8 file) {
|
||||
S8 data = os_read_file(scratch, file).error_is_fatal();
|
||||
PUSH_SIZE(scratch, 1);
|
||||
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;
|
||||
return result;
|
||||
}
|
||||
|
||||
33
ui.cpp
33
ui.cpp
@@ -4,9 +4,12 @@ enum UIWidgetKind {
|
||||
UIWidgetKind_Image,
|
||||
UIWidgetKind_Label,
|
||||
UIWidgetKind_Option,
|
||||
UIWidgetKind_Signal,
|
||||
UIWidgetKind_Group,
|
||||
};
|
||||
|
||||
#define UI_SIGNAL_CALLBACK(name) void name()
|
||||
typedef UI_SIGNAL_CALLBACK(UISignalCallback);
|
||||
struct UISetup {
|
||||
UIWidgetKind kind;
|
||||
S8 text;
|
||||
@@ -16,6 +19,7 @@ struct UISetup {
|
||||
B32 *b32;
|
||||
S8 *label;
|
||||
I32 *option;
|
||||
UISignalCallback *signal_callback;
|
||||
};
|
||||
I32 option_max;
|
||||
};
|
||||
@@ -23,6 +27,7 @@ struct UISetup {
|
||||
#define UI_IMAGE(x) {UIWidgetKind_Image,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_SIGNAL(text,x){UIWidgetKind_Signal,text,(void*)(x)}
|
||||
|
||||
struct UIWidget {
|
||||
UIWidgetKind kind;
|
||||
@@ -39,6 +44,7 @@ struct UIWidget {
|
||||
B32 *b32;
|
||||
S8 *label;
|
||||
I32 *option;
|
||||
UISignalCallback *signal_callback;
|
||||
} ptr;
|
||||
};
|
||||
|
||||
@@ -96,6 +102,13 @@ FUNCTION UIWidget *ui_push_option(Arena *arena, UIWidget *widget, S8 string, I32
|
||||
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) {
|
||||
UI result = {};
|
||||
result.arena = arena_sub(arena, MiB(16));
|
||||
@@ -114,6 +127,9 @@ FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) {
|
||||
case UIWidgetKind_Option: {
|
||||
ui_push_option(&result.arena, parent, s->text, s->option, s->option_max);
|
||||
} break;
|
||||
case UIWidgetKind_Signal: {
|
||||
ui_push_signal(&result.arena, parent, s->text, s->signal_callback);
|
||||
} break;
|
||||
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);
|
||||
pos.y -= rect.height - font->height;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user