From 59e47f8a3472885a97950dbf31e50b314e983ad1 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Sun, 27 Feb 2022 19:11:41 +0100 Subject: [PATCH] Testing, fixing premultiplied alpha, UI work, resizing plots --- README.md | 1 + main.cpp | 223 ++++++++++++++++++++++++++++++++-------------------- profile.cpp | 32 ++++---- ui.cpp | 176 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+), 102 deletions(-) create mode 100644 ui.cpp diff --git a/README.md b/README.md index 1b7dbd1..33951f0 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ box. In this box every pixel gets tested to see if it's in the triangle. In this the box is clipped to the image metrics - 0, 0, width, height. + ### Resources that helped me build the rasterizer (Might be helpful to you too): * Algorithm I used for triangle rasterization by Juan Pineda: https://www.cs.drexel.edu/~david/Classes/Papers/comp175-06-pineda.pdf diff --git a/main.cpp b/main.cpp index f505124..dc1facb 100644 --- a/main.cpp +++ b/main.cpp @@ -96,8 +96,7 @@ struct R_Render { #include "stb_image.h" #include -GLOBAL bool draw_rects = 0; -GLOBAL bool draw_wireframe = 0; +GLOBAL B32 draw_rects = 0; FUNCTION Vec4 srgb_to_almost_linear(Vec4 a) { @@ -111,6 +110,16 @@ FUNCTION return result; } +FUNCTION +Vec4 premultiplied_alpha(Vec4 dst, Vec4 src) { + Vec4 result; + result.r = src.r + ((1-src.a) * dst.r); + result.g = src.g + ((1-src.a) * dst.g); + result.b = src.b + ((1-src.a) * dst.b); + result.a = src.a + dst.a - src.a*dst.a; + return result; +} + FUNCTION void r_draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) { int max_x = (int)(MIN(X + w, dst->x) + 0.5f); @@ -118,63 +127,99 @@ void r_draw_rect(Bitmap* dst, F32 X, F32 Y, F32 w, F32 h, Vec4 color) { int min_x = (int)(MAX(0, X) + 0.5f); int min_y = (int)(MAX(0, Y) + 0.5f); + color.rgb *= color.a; color = srgb_to_almost_linear(color); for (int y = min_y; y < max_y; y++) { for (int x = min_x; x < max_x; x++) { U32 *dst_pixel = dst->pixels + (x + y * dst->x); - Vec4 dst_color = srgb_to_almost_linear(vec4abgr(*dst_pixel)); - - color.r = color.r + (1-color.a) * dst_color.r; - color.g = color.g + (1-color.a) * dst_color.g; - color.b = color.b + (1-color.a) * dst_color.b; - color.a = color.a + dst_color.a - color.a*dst_color.a; - U32 color32 = vec4_to_u32abgr(almost_linear_to_srgb(color)); + Vec4 dstc = srgb_to_almost_linear(vec4abgr(*dst_pixel)); + dstc = premultiplied_alpha(dstc, color); + U32 color32 = vec4_to_u32abgr(almost_linear_to_srgb(dstc)); *dst_pixel = color32; } } } FUNCTION -void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos) { +void r_draw_bitmap(Bitmap* dst, Bitmap* src, Vec2 pos, Vec2 size=vec2(F32MAX, F32MAX)) { I64 minx = (I64)(pos.x + 0.5); I64 miny = (I64)(pos.y + 0.5); - I64 maxx = minx + src->x; - I64 maxy = miny + src->y; - I64 offsetx = 0; - I64 offsety = 0; + + if (size.x == F32MAX || size.y == F32MAX) { + I64 maxx = minx + src->x; + I64 maxy = miny + src->y; + I64 offsetx = 0; + I64 offsety = 0; - if (maxx > dst->x) { - maxx = dst->x; - } - if (maxy > dst->y) { - maxy = dst->y; - } - if (minx < 0) { - offsetx = -minx; - minx = 0; - } - if (miny < 0) { - offsety = -miny; - miny = 0; - } - - for (I64 y = miny; y < maxy; y++) { - for (I64 x = minx; x < maxx; x++) { - I64 tx = x - minx + offsetx; - I64 ty = y - miny + offsety; - U32 *dst_pixel = dst->pixels + (x + y * dst->x); - U32 *pixel = src->pixels + (tx + ty * src->x); - Vec4 result_color = srgb_to_almost_linear(vec4abgr(*pixel)); - Vec4 dst_color = srgb_to_almost_linear(vec4abgr(*dst_pixel)); - result_color.r = result_color.r + (1-result_color.a) * dst_color.r; - result_color.g = result_color.g + (1-result_color.a) * dst_color.g; - result_color.b = result_color.b + (1-result_color.a) * dst_color.b; - result_color.a = result_color.a + dst_color.a - result_color.a*dst_color.a; - result_color = almost_linear_to_srgb(result_color); - U32 color32 = vec4_to_u32abgr(result_color); - *dst_pixel = color32; + if (maxx > dst->x) { + maxx = dst->x; + } + if (maxy > dst->y) { + maxy = dst->y; + } + if (minx < 0) { + offsetx = -minx; + minx = 0; + } + if (miny < 0) { + offsety = -miny; + miny = 0; + } + for (I64 y = miny; y < maxy; y++) { + for (I64 x = minx; x < maxx; x++) { + I64 tx = x - minx + offsetx; + I64 ty = y - miny + offsety; + U32 *dst_pixel = dst->pixels + (x + y * dst->x); + U32 *pixel = src->pixels + (tx + ty * src->x); + Vec4 result_color = srgb_to_almost_linear(vec4abgr(*pixel)); + Vec4 dst_color = srgb_to_almost_linear(vec4abgr(*dst_pixel)); + result_color = premultiplied_alpha(dst_color, result_color); + result_color = almost_linear_to_srgb(result_color); + U32 color32 = vec4_to_u32abgr(result_color); + *dst_pixel = color32; + } } } + else { + I64 maxx = minx + (I64)(size.x + 0.5f); + I64 maxy = miny + (I64)(size.y + 0.5f); + I64 offsetx = 0; + I64 offsety = 0; + + if (maxx > dst->x) { + maxx = dst->x; + } + if (maxy > dst->y) { + maxy = dst->y; + } + if (minx < 0) { + offsetx = -minx; + minx = 0; + } + if (miny < 0) { + offsety = -miny; + miny = 0; + } + F32 distx = (F32)(maxx - minx); + F32 disty = (F32)(maxy - miny); + for (I64 y = miny; y < maxy; y++) { + for (I64 x = minx; x < maxx; x++) { + F32 u = (F32)(x - minx) / distx; + F32 v = (F32)(y - miny) / disty; + I64 tx = (I64)(u * src->x + 0.5f); + I64 ty = (I64)(v * src->y + 0.5f); + U32 *dst_pixel = dst->pixels + (x + y * dst->x); + U32 *pixel = src->pixels + (tx + ty * src->x); + Vec4 result_color = srgb_to_almost_linear(vec4abgr(*pixel)); + Vec4 dst_color = srgb_to_almost_linear(vec4abgr(*dst_pixel)); + result_color = premultiplied_alpha(dst_color, result_color); + result_color = almost_linear_to_srgb(result_color); + U32 color32 = vec4_to_u32abgr(result_color); + *dst_pixel = color32; + } + } + } + } FN Vec4 r_base_string(Bitmap *dst, Font *font, S8 word, Vec2 pos, B32 draw) { @@ -267,10 +312,7 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 ligh result_color.r *= light; result_color.g *= light; result_color.b *= light; - result_color.r = result_color.r + (1-result_color.a) * dst_color.r; - result_color.g = result_color.g + (1-result_color.a) * dst_color.g; - result_color.b = result_color.b + (1-result_color.a) * dst_color.b; - result_color.a = result_color.a + dst_color.a - result_color.a*dst_color.a; + result_color = premultiplied_alpha(dst_color, result_color); result_color = almost_linear_to_srgb(result_color); U32 color32 = vec4_to_u32abgr(result_color); #else @@ -352,10 +394,7 @@ FUNCTION #if PREMULTIPLIED_ALPHA_BLENDING Vec4 dst_color = vec4abgr(*dst_pixel); dst_color = srgb_to_almost_linear(dst_color); - result_color.r = result_color.r + (1-result_color.a) * dst_color.r; - result_color.g = result_color.g + (1-result_color.a) * dst_color.g; - result_color.b = result_color.b + (1-result_color.a) * dst_color.b; - result_color.a = result_color.a + dst_color.a - result_color.a*dst_color.a; + result_color = premultiplied_alpha(dst_color, result_color); #endif // PREMULTIPLIED_ALPHA_BLENDING result_color = almost_linear_to_srgb(result_color); U32 color32 = vec4_to_u32abgr(result_color); @@ -372,24 +411,6 @@ FUNCTION } } -FUNCTION -void draw_line(Bitmap *dst, F32 x0, F32 y0, F32 x1, F32 y1) { - F32 delta_x = (x1 - x0); - F32 delta_y = (y1 - y0); - F32 longest_side_length = (ABS(delta_x) >= ABS(delta_y)) ? ABS(delta_x) : ABS(delta_y); - F32 x_inc = delta_x / (F32)longest_side_length; - F32 y_inc = delta_y / (F32)longest_side_length; - F32 current_x = (F32)x0; - F32 current_y = (F32)y0; - for (int i = 0; i <= longest_side_length; i++) { - int x = (int)(current_x + 0.5f); - int y = (int)(current_y + 0.5f); - dst->pixels[x + y * dst->x] = 0xffffffff; - current_x += x_inc; - current_y += y_inc; - } -} - FUNCTION Bitmap load_image(const char* path) { int x, y, n; @@ -411,7 +432,7 @@ Bitmap load_image(const char* path) { } FN void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) { - F64 min = FLT_MAX; + F64 min = F32MAX; F64 max = FLT_MIN; F64 step = dst->x / (F64)data_len; for (U32 i = 0; i < data_len; i++) { @@ -466,6 +487,7 @@ FN void r_draw_mesh(R_Render *r, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords Vec3 light_direction = mat4_rotation_y(45) * vec3(0, 0, 1); F32 light = -dot(normal, light_direction); light = CLAMP(0.05f, light, 1.f); + light = 1; if (dot(normal, p0_to_camera) > 0) { //@Note: Backface culling /// ## Clipping /// @@ -569,14 +591,10 @@ FN void r_draw_mesh(R_Render *r, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords } #endif - if (draw_wireframe) { - draw_line(&r->screen320, vert[0].pos.x, vert[0].pos.y, vert[1].pos.x, vert[1].pos.y); - draw_line(&r->screen320, vert[1].pos.x, vert[1].pos.y, vert[2].pos.x, vert[2].pos.y); - draw_line(&r->screen320, vert[2].pos.x, vert[2].pos.y, vert[0].pos.x, vert[0].pos.y); - } } } } +#include "ui.cpp" int main() { os.window_size.x = 1280; @@ -601,6 +619,7 @@ int main() { } scenario_name = LIT("assets/f22.obj"); + //scenario_name = LIT("assets/cube.obj"); //scenario_name = LIT("assets/AnyConv.com__White.obj"); //scenario_name = LIT("assets/sponza/sponza.obj"); Obj obj = load_obj(scenario_name); @@ -619,15 +638,46 @@ int main() { 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.depth320 = (F32 *)PUSH_SIZE(os.perm_arena, sizeof(F32) * screen_x * screen_y); - r.img = load_image("assets/bricksx64.png"); + r.img = load_image("assets/cat.png"); + + /* @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[] = { + 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 + } + + + + S8 frame_data = {}; + UISetup setup[] = { + UI_BOOL(LIT("Draw rectangles:"), &draw_rects), + UI_IMAGE(&r.plot), + UI_LABEL(&frame_data), + }; + UI ui = ui_make(os.perm_arena, setup, ARRAY_CAP(setup)); + B32 ui_mouse_lock = true; + while (os_game_loop()) { - r.camera_yaw.x += os.delta_mouse_pos.x * 0.05f; - r.camera_yaw.y -= os.delta_mouse_pos.y * 0.05f; + if (ui_mouse_lock == false) { + r.camera_yaw.x += os.delta_mouse_pos.x * 0.01f; + r.camera_yaw.y -= os.delta_mouse_pos.y * 0.01f; + } if (os.key[Key_Escape].pressed) os_quit(); if (os.key[Key_O].down) rotation += 0.05f; if (os.key[Key_P].down) rotation -= 0.05f; if (os.key[Key_F1].pressed) draw_rects = !draw_rects; - if (os.key[Key_F2].pressed) draw_wireframe = !draw_wireframe; + if (os.key[Key_F2].pressed) ui_mouse_lock = !ui_mouse_lock; if (os.key[Key_A].down) r.camera_pos.x -= speed * (F32)os.delta_time; if (os.key[Key_D].down) r.camera_pos.x += speed * (F32)os.delta_time; if (os.key[Key_W].down) { @@ -643,16 +693,17 @@ int main() { U32* p = r.screen320.pixels; for (int y = 0; y < r.screen320.y; y++) { for (int x = 0; x < r.screen320.x; x++) { - *p++ = 0x00333333; + *p++ = 0x33333333; } } F32* dp = r.depth320; for (int y = 0; y < r.screen320.y; y++) { for (int x = 0; x < r.screen320.x; x++) { - *dp++ = -FLT_MAX; + *dp++ = -F32MAX; } } + Mat4 camera_rotation = mat4_rotation_y(r.camera_yaw.x) * mat4_rotation_x(r.camera_yaw.y); r.camera_direction = (camera_rotation * vec4(0,0,1,1)).xyz; Vec3 target = r.camera_pos + r.camera_direction; @@ -676,12 +727,14 @@ int main() { *ptr++ = r.screen320.pixels[tx + ty * (r.screen320.x)]; } } - S8 print_string = string_format(os.frame_arena, "FPS:%f dt:%f frame:%u", os.fps, os.delta_time, os.frame); - r_draw_string(os.screen, &font, print_string, vec2(0, os.screen->y - font.height)); - if (r.plot_ready) r_draw_bitmap(os.screen, &r.plot, { 0, 0 }); + ui_end_frame(os.screen, &ui, &font); + frame_data = string_format(os.frame_arena, "FPS:%f dt:%f frame:%u", os.fps, os.delta_time, os.frame); + /*r_draw_string(os.screen, &font, print_string, vec2(0, os.screen->y - font.height)); + if (r.plot_ready) r_draw_bitmap(os.screen, &r.plot, { 0, 0 });*/ } } +///////////////////////////////////////////////////////////////////////////////////// /// ### Resources that helped me build the rasterizer (Might be helpful to you too): /// /// * Algorithm I used for triangle rasterization by Juan Pineda: https://www.cs.drexel.edu/~david/Classes/Papers/comp175-06-pineda.pdf diff --git a/profile.cpp b/profile.cpp index b6067bc..f86ad14 100644 --- a/profile.cpp +++ b/profile.cpp @@ -33,22 +33,20 @@ FN void save_profile_data(ProfileScope *scope, S8 scenario_name, S8 scope_name) } }*/ - { - Scratch scratch; - scenario_name = string_chop_last_period(scenario_name); - scenario_name = string_skip_to_last_slash(scenario_name); - U8 *string_pointer = string_begin(scratch); - S8 build_name = BUILD_NAME; - string_format(scratch, "%s %s\n", build_name, scenario_name); - I64 one_past_last = scope->i; - for (I64 si = 0; si < one_past_last; si++) { - string_format(scratch, "%f\n", scope->samples[si]); - } - - S8 data = string_end(scratch, string_pointer); - Date date = os_date(); - os_make_dir(LIT("stats")); - S8 name = string_format(scratch, "stats/%s_%s_%s_%u_%u_%u_%u_%u_%u.txt", scope_name, build_name, scenario_name, date.year, date.month, date.day, date.hour, date.minute, date.second); - os_append_file(name, data); + Scratch scratch; + scenario_name = string_chop_last_period(scenario_name); + scenario_name = string_skip_to_last_slash(scenario_name); + U8 *string_pointer = string_begin(scratch); + S8 build_name = BUILD_NAME; + string_format(scratch, "%s %s\n", build_name, scenario_name); + I64 one_past_last = scope->i; + for (I64 si = 0; si < one_past_last; si++) { + string_format(scratch, "%f\n", scope->samples[si]); } + + S8 data = string_end(scratch, string_pointer); + Date date = os_date(); + os_make_dir(LIT("stats")); + S8 name = string_format(scratch, "stats/%s_%s_%s_%u_%u_%u_%u_%u_%u.txt", scope_name, build_name, scenario_name, date.year, date.month, date.day, date.hour, date.minute, date.second); + os_append_file(name, data); } \ No newline at end of file diff --git a/ui.cpp b/ui.cpp new file mode 100644 index 0000000..1cf88f0 --- /dev/null +++ b/ui.cpp @@ -0,0 +1,176 @@ +enum UIWidgetKind { + UIWidgetKind_None, + UIWidgetKind_Boolean, + UIWidgetKind_Image, + UIWidgetKind_Label, + UIWidgetKind_Group, +}; + +struct UISetup { + UIWidgetKind kind; + S8 text; + union { + void *v; + Bitmap *image; + B32 *b32; + S8 *label; + }; +}; +#define UI_BOOL(text, x) {UIWidgetKind_Boolean,text,(void*)(x)} +#define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)} +#define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)} + +struct UIWidget { + UIWidgetKind kind; + UIWidget *next; + UIWidget *prev; + UIWidget *first_child; + UIWidget *last_child; + + S8 text; + Vec2 size; + union { + Bitmap *image; + B32 *b32; + S8 *label; + } ptr; +}; + +struct UI : UIWidget { + Arena arena; + + UIWidget *hot; + UIWidget *active; +}; + +FUNCTION UIWidget *ui_new_widget(Arena *arena, UIWidgetKind kind) { + UIWidget *result = PUSH_STRUCT(arena, UIWidget); + result->kind = kind; + return result; +} + +FUNCTION void ui_push_child(UIWidget *widget, UIWidget *child) { + DLL_QUEUE_PUSH(widget->first_child, widget->last_child, child); +} + +FUNCTION UIWidget *ui_push_child(Arena *arena, UIWidget *widget, UIWidgetKind kind) { + UIWidget *result = ui_new_widget(arena, kind); + ui_push_child(widget, result); + return result; +} + +FUNCTION UIWidget *ui_push_image(Arena *arena, UIWidget *widget, Bitmap *img) { + UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Image); + result->ptr.image = img; + + F32 ratio = (F32)result->ptr.image->x / (F32)result->ptr.image->y; + result->size.y = 64; + result->size.x = 64 * ratio; + return result; +} + +FUNCTION UIWidget *ui_push_bool(Arena *arena, UIWidget *widget, B32 *b32) { + UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Boolean); + result->ptr.b32 = b32; + return result; +} + +FUNCTION UIWidget *ui_push_string(Arena *arena, UIWidget *widget, S8 *string) { + UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Label); + result->ptr.label = string; + return result; +} + +FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) { + UI result = {}; + result.arena = arena_sub(arena, MiB(16)); + UIWidget *parent = &result; + for (UISetup *s = setup; s != (setup+len); s++) { + switch (s->kind) { + case UIWidgetKind_Image: { + ui_push_image(&result.arena, parent, s->image); + } break; + case UIWidgetKind_Boolean: { + ui_push_bool(&result.arena, parent, s->b32)->text = s->text; + } break; + case UIWidgetKind_Label: { + ui_push_string(&result.arena, parent, s->label); + } break; + INVALID_DEFAULT_CASE; + } + } + return result; +} + +FUNCTION B32 ui_mouse_test(UI *ui, UIWidget *w, Vec4 rect) { + B32 result = false; + if (os.mouse_pos.x > rect.x && os.mouse_pos.x < rect.x + rect.width && + os.mouse_pos.y > rect.y && os.mouse_pos.y < rect.y + rect.height) { + ui->hot = w; + if (os.key[Key_MouseLeft].down) { + ui->active = w; + } + } + else if (w == ui->hot) { + ui->hot = 0; + } + + if (os.key[Key_MouseLeft].released) { + if (ui->active == w) { + if (ui->hot == w) + result = true; + ui->active = 0; + } + } + + return result; +} + +FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) { + Scratch scratch; + Vec2 pos = vec2(0, (F32)dst->y); + for (UIWidget *w = ui->first_child; w; w = w->next) { + Vec4 rect = {}; + switch (w->kind) { + case UIWidgetKind_Image: { + pos.y -= w->size.y; + rect = vec4(pos, w->size); + ui_mouse_test(ui, w, rect); + S8 string = string_format(scratch, "%d %d", w->ptr.image->x, w->ptr.image->y); + r_draw_string(dst, font, string, pos); + r_draw_bitmap(dst, w->ptr.image, pos, w->size); + if (ui->active == w) { + F32 ratio = (F32)w->ptr.image->y / (F32)w->ptr.image->x; + w->size.x -= os.delta_mouse_pos.x; + w->size.y = w->size.x * ratio; + } + if (ui->hot == w) { + r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, vec4(1, 1, 1, 0.1f)); + } + } break; + case UIWidgetKind_Boolean: { + pos.y -= font->height; + Vec4 color = vec4(0, 0, 0, 1); + S8 string = string_format(scratch, "%s %d", w->text, *w->ptr.b32); + rect = r_get_string_rect(font, string, pos); + B32 clicked = ui_mouse_test(ui, w, rect); + if (clicked) *w->ptr.b32 = !*w->ptr.b32; + 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; + case UIWidgetKind_Label: { + pos.y -= font->height; + rect = r_draw_string(dst, font, *w->ptr.label, pos); + pos.y -= rect.height - font->height; + } break; + INVALID_DEFAULT_CASE; + } + + + } +} \ No newline at end of file