summaryrefslogtreecommitdiffstats
path: root/src/gui.cpp
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 19:36:24 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 19:36:24 +0100
commitb661a8dfd2c02bea54d8581ebb35d0fb3267867c (patch)
tree817760a933a14cc0e6446299424a017a9a1c88cf /src/gui.cpp
parentChams: keep additive viewmodel visible on bright backgrounds (diff)
downloadhl2h-b661a8dfd2c02bea54d8581ebb35d0fb3267867c.tar.gz
hl2h-b661a8dfd2c02bea54d8581ebb35d0fb3267867c.zip
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.
Diffstat (limited to 'src/gui.cpp')
-rw-r--r--src/gui.cpp126
1 files changed, 100 insertions, 26 deletions
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 {
195 const char *desc; 195 const char *desc;
196 bool *bval; // CK_TOGGLE 196 bool *bval; // CK_TOGGLE
197 int *ival; // CK_SLIDER 197 int *ival; // CK_SLIDER
198 int *rgb; // CK_COLOR: three ints, 0..255 198 int *rgb; // CK_COLOR: three or four ints, 0..255 (see has_alpha)
199 bool has_alpha; // CK_COLOR: rgb[3] is an editable alpha channel
199 const char *const *opts; // CK_DROPDOWN 200 const char *const *opts; // CK_DROPDOWN
200 int nopts; // CK_DROPDOWN 201 int nopts; // CK_DROPDOWN
201 int min, max; 202 int min, max;
@@ -256,6 +257,7 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc,
256 c.action = action; 257 c.action = action;
257 c.zero_unlimited = false; 258 c.zero_unlimited = false;
258 c.rgb = nullptr; 259 c.rgb = nullptr;
260 c.has_alpha = false;
259 c.opts = nullptr; 261 c.opts = nullptr;
260 c.nopts = 0; 262 c.nopts = 0;
261 c.hue = 0.0f; 263 c.hue = 0.0f;
@@ -287,6 +289,14 @@ static void add_color(int card, const char *label, const char *desc,
287 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); 289 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE);
288 g_ctrls[i].rgb = rgb; 290 g_ctrls[i].rgb = rgb;
289} 291}
292// Same picker, plus an editable alpha bar; rgb must hold four ints.
293static void add_color_alpha(int card, const char *label, const char *desc,
294 int *rgba) {
295 int i = g_nctrl;
296 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE);
297 g_ctrls[i].rgb = rgba;
298 g_ctrls[i].has_alpha = true;
299}
290static void add_dropdown(int card, const char *label, const char *desc, 300static void add_dropdown(int card, const char *label, const char *desc,
291 const char *const *opts, int nopts, int *value) { 301 const char *const *opts, int nopts, int *value) {
292 int i = g_nctrl; 302 int i = g_nctrl;
@@ -456,15 +466,15 @@ static void build_cards() {
456 &g.vis_on); 466 &g.vis_on);
457 add_dropdown(chams, "Visible material", "Material for the visible pass.", 467 add_dropdown(chams, "Visible material", "Material for the visible pass.",
458 chams_mat_names, 7, &g.vis_mat); 468 chams_mat_names, 7, &g.vis_mat);
459 add_color(chams, "Visible colour", "Colour for the visible pass.", 469 add_color_alpha(chams, "Visible colour", "Colour and alpha for the visible pass.",
460 g.vis_rgb); 470 g.vis_rgb);
461 add_toggle(chams, "Through walls", "Draw a pass with the depth test off.", 471 add_toggle(chams, "Through walls", "Draw a pass with the depth test off.",
462 &g.inv_on); 472 &g.inv_on);
463 add_dropdown(chams, "Invisible material", 473 add_dropdown(chams, "Invisible material",
464 "Material for the through-wall pass.", chams_mat_names, 7, 474 "Material for the through-wall pass.", chams_mat_names, 7,
465 &g.inv_mat); 475 &g.inv_mat);
466 add_color(chams, "Invisible colour", 476 add_color_alpha(chams, "Invisible colour",
467 "Colour for the through-wall pass.", g.inv_rgb); 477 "Colour and alpha for the through-wall pass.", g.inv_rgb);
468 }; 478 };
469 add_chams_group("Hostile", g_chams_hostile); 479 add_chams_group("Hostile", g_chams_hostile);
470 add_chams_group("Neutral", g_chams_neutral); 480 add_chams_group("Neutral", g_chams_neutral);
@@ -475,8 +485,8 @@ static void build_cards() {
475 &g_chams_viewmodel); 485 &g_chams_viewmodel);
476 add_dropdown(chams, "Viewmodel material", "Material for the viewmodel.", 486 add_dropdown(chams, "Viewmodel material", "Material for the viewmodel.",
477 chams_mat_names, 7, &g_chams_vm_mat); 487 chams_mat_names, 7, &g_chams_vm_mat);
478 add_color(chams, "Viewmodel colour", "Colour for the viewmodel.", 488 add_color_alpha(chams, "Viewmodel colour", "Colour and alpha for the viewmodel.",
479 g_chams_vm_rgb); 489 g_chams_vm_rgb);
480 int view = add_card(2, nullptr, "Camera / display", 490 int view = add_card(2, nullptr, "Camera / display",
481 "Camera FOV, viewmodel FOV and HDR brightness.", true); 491 "Camera FOV, viewmodel FOV and HDR brightness.", true);
482 add_slider(view, "Camera FOV", 492 add_slider(view, "Camera FOV",
@@ -970,7 +980,7 @@ static int button_w(const char *label) {
970 980
971static int label_row_h(const Ctrl &c); 981static int label_row_h(const Ctrl &c);
972static bool picker_open_for(const Ctrl &c); 982static bool picker_open_for(const Ctrl &c);
973static int picker_panel_h(); 983static int picker_panel_h(bool with_alpha);
974static bool card_in_category(int i); 984static bool card_in_category(int i);
975static Rect card_rect(int index, int y); 985static Rect card_rect(int index, int y);
976static int card_h(const Card &c); 986static int card_h(const Card &c);
@@ -978,7 +988,7 @@ static int card_h(const Card &c);
978static int row_h(const Ctrl &c) { 988static int row_h(const Ctrl &c) {
979 int h = label_row_h(c); 989 int h = label_row_h(c);
980 if (picker_open_for(c)) 990 if (picker_open_for(c))
981 h += picker_panel_h(); 991 h += picker_panel_h(c.has_alpha);
982 return h; 992 return h;
983} 993}
984 994
@@ -989,6 +999,7 @@ static int row_h(const Ctrl &c) {
989static constexpr int PICKER_SV_W = 180; 999static constexpr int PICKER_SV_W = 180;
990static constexpr int PICKER_SV_H = 110; 1000static constexpr int PICKER_SV_H = 110;
991static constexpr int PICKER_HUE_H = 12; 1001static constexpr int PICKER_HUE_H = 12;
1002static constexpr int PICKER_ALPHA_H = 12;
992static constexpr int PICKER_GAP = 8; 1003static constexpr int PICKER_GAP = 8;
993 1004
994static bool picker_open_for(const Ctrl &c) { 1005static bool picker_open_for(const Ctrl &c) {
@@ -996,9 +1007,11 @@ static bool picker_open_for(const Ctrl &c) {
996 &c == &g_ctrls[g_picker_ctrl]; 1007 &c == &g_ctrls[g_picker_ctrl];
997} 1008}
998 1009
999static int picker_panel_h() { 1010static int picker_panel_h(bool with_alpha) {
1000 return S((float)(PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H + 1011 int h = PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H + PICKER_GAP;
1001 PICKER_GAP)); 1012 if (with_alpha)
1013 h += PICKER_ALPHA_H + PICKER_GAP;
1014 return S((float)h);
1002} 1015}
1003 1016
1004static int label_row_h(const Ctrl &c) { 1017static 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) {
1069} 1082}
1070 1083
1071struct PickerRects { 1084struct PickerRects {
1072 Rect panel, sv, hue, preview; 1085 Rect panel, sv, hue, alpha, preview;
1073}; 1086};
1074 1087
1075static PickerRects picker_rects(const Rect &cr, int row_y, int base_h) { 1088static PickerRects picker_rects(const Rect &cr, int row_y, int base_h,
1089 bool with_alpha) {
1076 PickerRects p{}; 1090 PickerRects p{};
1077 int px = cr.x + S(16.0f); 1091 int px = cr.x + S(16.0f);
1078 int py = row_y + base_h; 1092 int py = row_y + base_h;
1079 int pw = card_content_w() - S(32.0f); 1093 int pw = card_content_w() - S(32.0f);
1080 if (pw > S(340.0f)) pw = S(340.0f); 1094 if (pw > S(340.0f)) pw = S(340.0f);
1081 p.panel = Rect{px, py, pw, picker_panel_h()}; 1095 p.panel = Rect{px, py, pw, picker_panel_h(with_alpha)};
1082 int preview_w = S(84.0f); 1096 int preview_w = S(84.0f);
1083 int svw = S((float)PICKER_SV_W); 1097 int svw = S((float)PICKER_SV_W);
1084 if (svw + preview_w + S(16.0f) > pw) 1098 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) {
1089 S((float)PICKER_SV_H)}; 1103 S((float)PICKER_SV_H)};
1090 p.hue = Rect{p.sv.x, p.sv.y + p.sv.h + S(PICKER_GAP), svw, 1104 p.hue = Rect{p.sv.x, p.sv.y + p.sv.h + S(PICKER_GAP), svw,
1091 S((float)PICKER_HUE_H)}; 1105 S((float)PICKER_HUE_H)};
1106 p.alpha = Rect{p.hue.x, p.hue.y + p.hue.h + S(PICKER_GAP), svw,
1107 S((float)PICKER_ALPHA_H)};
1092 p.preview = Rect{p.sv.x + p.sv.w + S(12.0f), p.sv.y, preview_w, S(52.0f)}; 1108 p.preview = Rect{p.sv.x + p.sv.w + S(12.0f), p.sv.y, preview_w, S(52.0f)};
1093 return p; 1109 return p;
1094} 1110}
@@ -1175,7 +1191,7 @@ static bool picker_rects_for(int ctrl_index, PickerRects &out) {
1175 if (!control_rect(ctrl_index, card, row)) 1191 if (!control_rect(ctrl_index, card, row))
1176 return false; 1192 return false;
1177 Ctrl &ct = g_ctrls[ctrl_index]; 1193 Ctrl &ct = g_ctrls[ctrl_index];
1178 out = picker_rects(card, row.y, label_row_h(ct)); 1194 out = picker_rects(card, row.y, label_row_h(ct), ct.has_alpha);
1179 return true; 1195 return true;
1180} 1196}
1181 1197
@@ -1191,18 +1207,47 @@ static void picker_apply(Ctrl &ct, const PickerRects &p) {
1191 if (v > 1.0f) v = 1.0f; 1207 if (v > 1.0f) v = 1.0f;
1192 ct.sat = s; 1208 ct.sat = s;
1193 ct.val = v; 1209 ct.val = v;
1210 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1194 } else if (g_picker_drag == 2) { 1211 } else if (g_picker_drag == 2) {
1195 float h = 360.0f * (float)(g_mouse_x - p.hue.x) / (float)p.hue.w; 1212 float h = 360.0f * (float)(g_mouse_x - p.hue.x) / (float)p.hue.w;
1196 if (h < 0.0f) h = 0.0f; 1213 if (h < 0.0f) h = 0.0f;
1197 if (h >= 360.0f) h = 359.9f; 1214 if (h >= 360.0f) h = 359.9f;
1198 ct.hue = h; 1215 ct.hue = h;
1216 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1217 } else if (g_picker_drag == 3 && ct.has_alpha) {
1218 if (p.alpha.w <= 0)
1219 return;
1220 int a = (int)(255.0f * (float)(g_mouse_x - p.alpha.x) /
1221 (float)p.alpha.w +
1222 0.5f);
1223 if (a < 0) a = 0;
1224 if (a > 255) a = 255;
1225 ct.rgb[3] = a;
1226 }
1227}
1228
1229static void draw_checker(const Rect &r, const Rect *clip) {
1230 int sq = S(6.0f);
1231 if (sq < 4)
1232 sq = 4;
1233 for (int yy = 0; yy < r.h; yy += sq) {
1234 for (int xx = 0; xx < r.w; xx += sq) {
1235 int w = sq < r.w - xx ? sq : r.w - xx;
1236 int h = sq < r.h - yy ? sq : r.h - yy;
1237 bool odd = ((xx / sq) + (yy / sq)) & 1;
1238 fill(Rect{r.x + xx, r.y + yy, w, h},
1239 odd ? Col{62, 66, 74, 255} : Col{38, 41, 47, 255}, clip);
1240 }
1199 } 1241 }
1200 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1201} 1242}
1202 1243
1203static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct, 1244static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct,
1204 int row_y) { 1245 int row_y) {
1205 PickerRects p = picker_rects(cr, row_y, label_row_h(ct)); 1246 PickerRects p = picker_rects(cr, row_y, label_row_h(ct), ct.has_alpha);
1247 int pr = ct.rgb ? ct.rgb[0] : 255;
1248 int pg = ct.rgb ? ct.rgb[1] : 255;
1249 int pb = ct.rgb ? ct.rgb[2] : 255;
1250 int pa = (ct.has_alpha && ct.rgb) ? ct.rgb[3] : 255;
1206 fill(p.panel, Col{20, 23, 29, 250}, &clip); 1251 fill(p.panel, Col{20, 23, 29, 250}, &clip);
1207 outline(p.panel, C_BORDER, &clip); 1252 outline(p.panel, C_BORDER, &clip);
1208 1253
@@ -1236,6 +1281,23 @@ static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct,
1236 } 1281 }
1237 outline(p.hue, Col{0, 0, 0, 255}, &clip); 1282 outline(p.hue, Col{0, 0, 0, 255}, &clip);
1238 1283
1284 // Alpha bar: checkerboard behind a transparent->opaque colour gradient.
1285 if (ct.has_alpha) {
1286 draw_checker(p.alpha, &clip);
1287 int ax = S(4.0f);
1288 if (ax < 2) ax = 2;
1289 for (int xx = 0; xx < p.alpha.w; xx += ax) {
1290 int a = p.alpha.w > 1 ? 255 * xx / (p.alpha.w - 1) : 255;
1291 int rw = ax < p.alpha.w - xx ? ax : p.alpha.w - xx;
1292 fill(Rect{p.alpha.x + xx, p.alpha.y, rw, p.alpha.h},
1293 Col{pr, pg, pb, a}, &clip);
1294 }
1295 outline(p.alpha, Col{0, 0, 0, 255}, &clip);
1296 int amx = p.alpha.x + (int)((float)pa / 255.0f * (float)p.alpha.w);
1297 line(amx, p.alpha.y - S(2.0f), amx, p.alpha.y + p.alpha.h + S(2.0f),
1298 Col{255, 255, 255, 255});
1299 }
1300
1239 // Selection markers. 1301 // Selection markers.
1240 int mx = p.sv.x + (int)(ct.sat * (float)p.sv.w); 1302 int mx = p.sv.x + (int)(ct.sat * (float)p.sv.w);
1241 int my = p.sv.y + (int)((1.0f - ct.val) * (float)p.sv.h); 1303 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,
1249 Col{255, 255, 255, 255}); 1311 Col{255, 255, 255, 255});
1250 1312
1251 // Preview swatch and hex value. 1313 // Preview swatch and hex value.
1252 int pr = ct.rgb ? ct.rgb[0] : 255; 1314 draw_checker(p.preview, &clip);
1253 int pg = ct.rgb ? ct.rgb[1] : 255; 1315 fill(p.preview, Col{pr, pg, pb, pa}, &clip);
1254 int pb = ct.rgb ? ct.rgb[2] : 255;
1255 fill(p.preview, Col{pr, pg, pb, 255}, &clip);
1256 outline(p.preview, C_BORDER, &clip); 1316 outline(p.preview, C_BORDER, &clip);
1257 char hex[16]; 1317 char hex[16];
1258 snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb); 1318 if (ct.has_alpha)
1319 snprintf(hex, sizeof(hex), "#%02X%02X%02X%02X", pr, pg, pb, pa);
1320 else
1321 snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb);
1259 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f), hex, 1322 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f), hex,
1260 g_font_body, g_body_h, p.preview.w, C_TEXT); 1323 g_font_body, g_body_h, p.preview.w, C_TEXT);
1261 char rgb[48]; 1324 char rgb[48];
1262 snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb); 1325 if (ct.has_alpha)
1326 snprintf(rgb, sizeof(rgb), "%d %d %d %d", pr, pg, pb, pa);
1327 else
1328 snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb);
1263 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f) + g_body_h, 1329 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f) + g_body_h,
1264 rgb, g_font_body, g_body_h, p.preview.w, C_DIM); 1330 rgb, g_font_body, g_body_h, p.preview.w, C_DIM);
1265} 1331}
@@ -1581,7 +1647,7 @@ static void handle_mouse_click() {
1581 int base = label_row_h(ct); 1647 int base = label_row_h(ct);
1582 bool is_open = (g_picker_ctrl == idx); 1648 bool is_open = (g_picker_ctrl == idx);
1583 if (is_open) { 1649 if (is_open) {
1584 PickerRects p = picker_rects(cr, by, base); 1650 PickerRects p = picker_rects(cr, by, base, ct.has_alpha);
1585 if (p.sv.has(mx, my)) { 1651 if (p.sv.has(mx, my)) {
1586 g_picker_drag = 1; 1652 g_picker_drag = 1;
1587 picker_apply(g_ctrls[idx], p); 1653 picker_apply(g_ctrls[idx], p);
@@ -1592,6 +1658,11 @@ static void handle_mouse_click() {
1592 picker_apply(g_ctrls[idx], p); 1658 picker_apply(g_ctrls[idx], p);
1593 return; 1659 return;
1594 } 1660 }
1661 if (ct.has_alpha && p.alpha.has(mx, my)) {
1662 g_picker_drag = 3;
1663 picker_apply(g_ctrls[idx], p);
1664 return;
1665 }
1595 } 1666 }
1596 Rect sw{cr.x + cr.w - S(96.0f), by + (base - S(20.0f)) / 2, 1667 Rect sw{cr.x + cr.w - S(96.0f), by + (base - S(20.0f)) / 2,
1597 S(72.0f), S(20.0f)}; 1668 S(72.0f), S(20.0f)};
@@ -2015,9 +2086,12 @@ static void draw_card(const Rect &clip, int index, int y) {
2015 int pr = ct.rgb ? ct.rgb[0] : 255; 2086 int pr = ct.rgb ? ct.rgb[0] : 255;
2016 int pg = ct.rgb ? ct.rgb[1] : 255; 2087 int pg = ct.rgb ? ct.rgb[1] : 255;
2017 int pb = ct.rgb ? ct.rgb[2] : 255; 2088 int pb = ct.rgb ? ct.rgb[2] : 255;
2089 int pa = (ct.has_alpha && ct.rgb) ? ct.rgb[3] : 255;
2018 Rect sw{cr.x + cr.w - S(96.0f), 2090 Rect sw{cr.x + cr.w - S(96.0f),
2019 by + (base_h - S(20.0f)) / 2, S(72.0f), S(20.0f)}; 2091 by + (base_h - S(20.0f)) / 2, S(72.0f), S(20.0f)};
2020 fill(sw, Col{pr, pg, pb, 255}, &clip); 2092 if (pa < 255)
2093 draw_checker(sw, &clip);
2094 fill(sw, Col{pr, pg, pb, pa}, &clip);
2021 outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip); 2095 outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip);
2022 if (picker_open_for(ct)) 2096 if (picker_open_for(ct))
2023 draw_picker(clip, cr, ct, by); 2097 draw_picker(clip, cr, ct, by);