From b661a8dfd2c02bea54d8581ebb35d0fb3267867c Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 19:36:24 +0100 Subject: Chams: per-colour alpha and a softer viewmodel additive base - Colour picker gains an optional alpha bar (checkerboard preview, #RRGGBBAA readout); add_color_alpha is used for the chams colours. Chams colours are now r,g,b,a and chams_apply sets $alpha plus MATERIAL_VAR_TRANSLUCENT when alpha < 255. Config stores vis_a / inv_a / vm a. - The additive viewmodel base is now a faint translucent tint (alpha/8) instead of an opaque fill, so additive/Fullbright stay see-through and the viewmodel no longer becomes a solid blob. Fullbright whitening reduced to 25% so blue stays blue instead of turning lavender. --- src/gui.cpp | 126 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 26 deletions(-) (limited to 'src/gui.cpp') diff --git a/src/gui.cpp b/src/gui.cpp index 3cbb40a..89888da 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -195,7 +195,8 @@ struct Ctrl { const char *desc; bool *bval; // CK_TOGGLE int *ival; // CK_SLIDER - int *rgb; // CK_COLOR: three ints, 0..255 + int *rgb; // CK_COLOR: three or four ints, 0..255 (see has_alpha) + bool has_alpha; // CK_COLOR: rgb[3] is an editable alpha channel const char *const *opts; // CK_DROPDOWN int nopts; // CK_DROPDOWN int min, max; @@ -256,6 +257,7 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc, c.action = action; c.zero_unlimited = false; c.rgb = nullptr; + c.has_alpha = false; c.opts = nullptr; c.nopts = 0; c.hue = 0.0f; @@ -287,6 +289,14 @@ static void add_color(int card, const char *label, const char *desc, add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); g_ctrls[i].rgb = rgb; } +// Same picker, plus an editable alpha bar; rgb must hold four ints. +static void add_color_alpha(int card, const char *label, const char *desc, + int *rgba) { + int i = g_nctrl; + add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); + g_ctrls[i].rgb = rgba; + g_ctrls[i].has_alpha = true; +} static void add_dropdown(int card, const char *label, const char *desc, const char *const *opts, int nopts, int *value) { int i = g_nctrl; @@ -456,15 +466,15 @@ static void build_cards() { &g.vis_on); add_dropdown(chams, "Visible material", "Material for the visible pass.", chams_mat_names, 7, &g.vis_mat); - add_color(chams, "Visible colour", "Colour for the visible pass.", - g.vis_rgb); + add_color_alpha(chams, "Visible colour", "Colour and alpha for the visible pass.", + g.vis_rgb); add_toggle(chams, "Through walls", "Draw a pass with the depth test off.", &g.inv_on); add_dropdown(chams, "Invisible material", "Material for the through-wall pass.", chams_mat_names, 7, &g.inv_mat); - add_color(chams, "Invisible colour", - "Colour for the through-wall pass.", g.inv_rgb); + add_color_alpha(chams, "Invisible colour", + "Colour and alpha for the through-wall pass.", g.inv_rgb); }; add_chams_group("Hostile", g_chams_hostile); add_chams_group("Neutral", g_chams_neutral); @@ -475,8 +485,8 @@ static void build_cards() { &g_chams_viewmodel); add_dropdown(chams, "Viewmodel material", "Material for the viewmodel.", chams_mat_names, 7, &g_chams_vm_mat); - add_color(chams, "Viewmodel colour", "Colour for the viewmodel.", - g_chams_vm_rgb); + add_color_alpha(chams, "Viewmodel colour", "Colour and alpha for the viewmodel.", + g_chams_vm_rgb); int view = add_card(2, nullptr, "Camera / display", "Camera FOV, viewmodel FOV and HDR brightness.", true); add_slider(view, "Camera FOV", @@ -970,7 +980,7 @@ static int button_w(const char *label) { static int label_row_h(const Ctrl &c); static bool picker_open_for(const Ctrl &c); -static int picker_panel_h(); +static int picker_panel_h(bool with_alpha); static bool card_in_category(int i); static Rect card_rect(int index, int y); static int card_h(const Card &c); @@ -978,7 +988,7 @@ static int card_h(const Card &c); static int row_h(const Ctrl &c) { int h = label_row_h(c); if (picker_open_for(c)) - h += picker_panel_h(); + h += picker_panel_h(c.has_alpha); return h; } @@ -989,6 +999,7 @@ static int row_h(const Ctrl &c) { static constexpr int PICKER_SV_W = 180; static constexpr int PICKER_SV_H = 110; static constexpr int PICKER_HUE_H = 12; +static constexpr int PICKER_ALPHA_H = 12; static constexpr int PICKER_GAP = 8; static bool picker_open_for(const Ctrl &c) { @@ -996,9 +1007,11 @@ static bool picker_open_for(const Ctrl &c) { &c == &g_ctrls[g_picker_ctrl]; } -static int picker_panel_h() { - return S((float)(PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H + - PICKER_GAP)); +static int picker_panel_h(bool with_alpha) { + int h = PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H + PICKER_GAP; + if (with_alpha) + h += PICKER_ALPHA_H + PICKER_GAP; + return S((float)h); } static int label_row_h(const Ctrl &c) { @@ -1069,16 +1082,17 @@ static void rgb_to_hsv(int r, int g, int b, float &h, float &s, float &v) { } struct PickerRects { - Rect panel, sv, hue, preview; + Rect panel, sv, hue, alpha, preview; }; -static PickerRects picker_rects(const Rect &cr, int row_y, int base_h) { +static PickerRects picker_rects(const Rect &cr, int row_y, int base_h, + bool with_alpha) { PickerRects p{}; int px = cr.x + S(16.0f); int py = row_y + base_h; int pw = card_content_w() - S(32.0f); if (pw > S(340.0f)) pw = S(340.0f); - p.panel = Rect{px, py, pw, picker_panel_h()}; + p.panel = Rect{px, py, pw, picker_panel_h(with_alpha)}; int preview_w = S(84.0f); int svw = S((float)PICKER_SV_W); if (svw + preview_w + S(16.0f) > pw) @@ -1089,6 +1103,8 @@ static PickerRects picker_rects(const Rect &cr, int row_y, int base_h) { S((float)PICKER_SV_H)}; p.hue = Rect{p.sv.x, p.sv.y + p.sv.h + S(PICKER_GAP), svw, S((float)PICKER_HUE_H)}; + p.alpha = Rect{p.hue.x, p.hue.y + p.hue.h + S(PICKER_GAP), svw, + S((float)PICKER_ALPHA_H)}; p.preview = Rect{p.sv.x + p.sv.w + S(12.0f), p.sv.y, preview_w, S(52.0f)}; return p; } @@ -1175,7 +1191,7 @@ static bool picker_rects_for(int ctrl_index, PickerRects &out) { if (!control_rect(ctrl_index, card, row)) return false; Ctrl &ct = g_ctrls[ctrl_index]; - out = picker_rects(card, row.y, label_row_h(ct)); + out = picker_rects(card, row.y, label_row_h(ct), ct.has_alpha); return true; } @@ -1191,18 +1207,47 @@ static void picker_apply(Ctrl &ct, const PickerRects &p) { if (v > 1.0f) v = 1.0f; ct.sat = s; ct.val = v; + hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]); } else if (g_picker_drag == 2) { float h = 360.0f * (float)(g_mouse_x - p.hue.x) / (float)p.hue.w; if (h < 0.0f) h = 0.0f; if (h >= 360.0f) h = 359.9f; ct.hue = h; + hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]); + } else if (g_picker_drag == 3 && ct.has_alpha) { + if (p.alpha.w <= 0) + return; + int a = (int)(255.0f * (float)(g_mouse_x - p.alpha.x) / + (float)p.alpha.w + + 0.5f); + if (a < 0) a = 0; + if (a > 255) a = 255; + ct.rgb[3] = a; + } +} + +static void draw_checker(const Rect &r, const Rect *clip) { + int sq = S(6.0f); + if (sq < 4) + sq = 4; + for (int yy = 0; yy < r.h; yy += sq) { + for (int xx = 0; xx < r.w; xx += sq) { + int w = sq < r.w - xx ? sq : r.w - xx; + int h = sq < r.h - yy ? sq : r.h - yy; + bool odd = ((xx / sq) + (yy / sq)) & 1; + fill(Rect{r.x + xx, r.y + yy, w, h}, + odd ? Col{62, 66, 74, 255} : Col{38, 41, 47, 255}, clip); + } } - hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]); } static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct, int row_y) { - PickerRects p = picker_rects(cr, row_y, label_row_h(ct)); + PickerRects p = picker_rects(cr, row_y, label_row_h(ct), ct.has_alpha); + int pr = ct.rgb ? ct.rgb[0] : 255; + int pg = ct.rgb ? ct.rgb[1] : 255; + int pb = ct.rgb ? ct.rgb[2] : 255; + int pa = (ct.has_alpha && ct.rgb) ? ct.rgb[3] : 255; fill(p.panel, Col{20, 23, 29, 250}, &clip); outline(p.panel, C_BORDER, &clip); @@ -1236,6 +1281,23 @@ static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct, } outline(p.hue, Col{0, 0, 0, 255}, &clip); + // Alpha bar: checkerboard behind a transparent->opaque colour gradient. + if (ct.has_alpha) { + draw_checker(p.alpha, &clip); + int ax = S(4.0f); + if (ax < 2) ax = 2; + for (int xx = 0; xx < p.alpha.w; xx += ax) { + int a = p.alpha.w > 1 ? 255 * xx / (p.alpha.w - 1) : 255; + int rw = ax < p.alpha.w - xx ? ax : p.alpha.w - xx; + fill(Rect{p.alpha.x + xx, p.alpha.y, rw, p.alpha.h}, + Col{pr, pg, pb, a}, &clip); + } + outline(p.alpha, Col{0, 0, 0, 255}, &clip); + int amx = p.alpha.x + (int)((float)pa / 255.0f * (float)p.alpha.w); + line(amx, p.alpha.y - S(2.0f), amx, p.alpha.y + p.alpha.h + S(2.0f), + Col{255, 255, 255, 255}); + } + // Selection markers. int mx = p.sv.x + (int)(ct.sat * (float)p.sv.w); int my = p.sv.y + (int)((1.0f - ct.val) * (float)p.sv.h); @@ -1249,17 +1311,21 @@ static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct, Col{255, 255, 255, 255}); // Preview swatch and hex value. - int pr = ct.rgb ? ct.rgb[0] : 255; - int pg = ct.rgb ? ct.rgb[1] : 255; - int pb = ct.rgb ? ct.rgb[2] : 255; - fill(p.preview, Col{pr, pg, pb, 255}, &clip); + draw_checker(p.preview, &clip); + fill(p.preview, Col{pr, pg, pb, pa}, &clip); outline(p.preview, C_BORDER, &clip); char hex[16]; - snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb); + if (ct.has_alpha) + snprintf(hex, sizeof(hex), "#%02X%02X%02X%02X", pr, pg, pb, pa); + else + snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb); text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f), hex, g_font_body, g_body_h, p.preview.w, C_TEXT); char rgb[48]; - snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb); + if (ct.has_alpha) + snprintf(rgb, sizeof(rgb), "%d %d %d %d", pr, pg, pb, pa); + else + snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb); text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f) + g_body_h, rgb, g_font_body, g_body_h, p.preview.w, C_DIM); } @@ -1581,7 +1647,7 @@ static void handle_mouse_click() { int base = label_row_h(ct); bool is_open = (g_picker_ctrl == idx); if (is_open) { - PickerRects p = picker_rects(cr, by, base); + PickerRects p = picker_rects(cr, by, base, ct.has_alpha); if (p.sv.has(mx, my)) { g_picker_drag = 1; picker_apply(g_ctrls[idx], p); @@ -1592,6 +1658,11 @@ static void handle_mouse_click() { picker_apply(g_ctrls[idx], p); return; } + if (ct.has_alpha && p.alpha.has(mx, my)) { + g_picker_drag = 3; + picker_apply(g_ctrls[idx], p); + return; + } } Rect sw{cr.x + cr.w - S(96.0f), by + (base - S(20.0f)) / 2, S(72.0f), S(20.0f)}; @@ -2015,9 +2086,12 @@ static void draw_card(const Rect &clip, int index, int y) { int pr = ct.rgb ? ct.rgb[0] : 255; int pg = ct.rgb ? ct.rgb[1] : 255; int pb = ct.rgb ? ct.rgb[2] : 255; + int pa = (ct.has_alpha && ct.rgb) ? ct.rgb[3] : 255; Rect sw{cr.x + cr.w - S(96.0f), by + (base_h - S(20.0f)) / 2, S(72.0f), S(20.0f)}; - fill(sw, Col{pr, pg, pb, 255}, &clip); + if (pa < 255) + draw_checker(sw, &clip); + fill(sw, Col{pr, pg, pb, pa}, &clip); outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip); if (picker_open_for(ct)) draw_picker(clip, cr, ct, by); -- cgit v1.2.3