summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--CONTEXT.md26
-rw-r--r--src/gui.cpp126
-rw-r--r--src/hl2h.h7
-rw-r--r--src/menu.cpp50
-rw-r--r--src/studio.cpp35
5 files changed, 178 insertions, 66 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index c75e36c..1bb0a6a 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -290,11 +290,16 @@ vocabulary / decisions.
290 viewmodel entities (class contains `viewmodel` or model path `.../v_...`) 290 viewmodel entities (class contains `viewmodel` or model path `.../v_...`)
291 are a fourth group. 291 are a fourth group.
292- Each relationship has independent **visible** and **through-wall** settings: 292- Each relationship has independent **visible** and **through-wall** settings:
293 on/off, style, and colour. The viewmodel has on/off, style and colour only. 293 on/off, style, colour and alpha. The viewmodel has on/off, style, colour and
294 The through-wall pass uses `MATERIAL_VAR_IGNOREZ` (1 << 15) through 294 alpha only. The through-wall pass uses `MATERIAL_VAR_IGNOREZ` (1 << 15)
295 `IMaterial::SetMaterialVarFlag` (slot 29); colours use `$color`/`$color2` 295 through `IMaterial::SetMaterialVarFlag` (slot 29); colours use
296 (IMaterialVar slot 11, SetVecValue 3-float slot 11) and `ColorModulate` 296 `$color`/`$color2` (IMaterialVar slot 11, SetVecValue 3-float slot 11) and
297 (slot 28). Shared materials are restored after each draw. 297 `ColorModulate` (slot 28). Alpha uses `$alpha` (IMaterialVar slot 3,
298 SetFloatValue) and `MATERIAL_VAR_TRANSLUCENT` (1 << 21) when alpha < 255.
299 Shared materials are restored after each draw.
300- The GUI colour picker (`add_color_alpha`) has an alpha bar, checkerboard
301 preview and `#RRGGBBAA` readout; plain `add_color` keeps the old opaque
302 three-component behaviour. Chams colours are four ints (r, g, b, a).
298- Styles are built from `debug/debugtranslucentsinglecolor` (UnlitGeneric, 303- Styles are built from `debug/debugtranslucentsinglecolor` (UnlitGeneric,
299 `$color`) and material flags: 304 `$color`) and material flags:
300 0 Flat (solid + FLAT), 1 Wireframe (`debug/debugwireframe`), 2 Additive 305 0 Flat (solid + FLAT), 1 Wireframe (`debug/debugwireframe`), 2 Additive
@@ -310,12 +315,13 @@ vocabulary / decisions.
310- Viewmodels are collated by the engine as `RENDER_GROUP_VIEW_MODEL_OPAQUE`, 315- Viewmodels are collated by the engine as `RENDER_GROUP_VIEW_MODEL_OPAQUE`,
311 so an additive forced material can wash out against a bright sky or wall 316 so an additive forced material can wash out against a bright sky or wall
312 (and the engine may not treat the draw as translucent). Additive styles on 317 (and the engine may not treat the draw as translucent). Additive styles on
313 the viewmodel therefore draw a dim opaque solid base first, then the 318 the viewmodel therefore draw a faint translucent base (alpha/8) first, then
314 additive pass; the first-person model never disappears. 319 the additive pass, so the model keeps a silhouette on bright backgrounds
320 without becoming an opaque blob.
315- Feature is off by default. Config sections: `[chams]` (active/bind), 321- Feature is off by default. Config sections: `[chams]` (active/bind),
316 `[chams_hostile|neutral|friendly]` (`vis`, `vis_mat`, `vis_r/g/b`, `inv`, 322 `[chams_hostile|neutral|friendly]` (`vis`, `vis_mat`, `vis_r/g/b/a`, `inv`,
317 `inv_mat`, `inv_r/g/b`) and `[chams_vm]` (`on`, `mat`, `r/g/b`). The GUI has 323 `inv_mat`, `inv_r/g/b/a`) and `[chams_vm]` (`on`, `mat`, `r/g/b/a`). The GUI
318 a Hostile/Neutral/Friendly section each, plus Viewmodel. 324 has a Hostile/Neutral/Friendly section each, plus Viewmodel.
319- This is a material override, not stencil outlines. 325- This is a material override, not stencil outlines.
320 326
321## Bones / ESP 327## Bones / ESP
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);
diff --git a/src/hl2h.h b/src/hl2h.h
index 4c9012f..f7274f1 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -236,15 +236,16 @@ extern bool g_proj_fuse, g_proj_radius;
236struct ChamsGroup { 236struct ChamsGroup {
237 bool vis_on; 237 bool vis_on;
238 int vis_mat; 238 int vis_mat;
239 int vis_rgb[3]; 239 int vis_rgb[4]; // r, g, b, a (0..255)
240 bool inv_on; 240 bool inv_on;
241 int inv_mat; 241 int inv_mat;
242 int inv_rgb[3]; 242 int inv_rgb[4];
243}; 243};
244extern ChamsGroup g_chams_hostile, g_chams_neutral, g_chams_friendly; 244extern ChamsGroup g_chams_hostile, g_chams_neutral, g_chams_friendly;
245extern bool g_chams_viewmodel; 245extern bool g_chams_viewmodel;
246extern int g_chams_vm_mat; 246extern int g_chams_vm_mat;
247extern int g_chams_vm_rgb[3];extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; 247extern int g_chams_vm_rgb[4];
248extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on;
248extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; 249extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3];
249extern bool g_fog_skybox, g_fog_disable; 250extern bool g_fog_skybox, g_fog_disable;
250extern int g_fog_rgb[3], g_fog_start, g_fog_end; 251extern int g_fog_rgb[3], g_fog_start, g_fog_end;
diff --git a/src/menu.cpp b/src/menu.cpp
index bdb9531..873f5ed 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -64,12 +64,15 @@ FeatureState g_projectiles{true, 0}; // grenade / rocket ESP
64bool g_proj_fuse = true; 64bool g_proj_fuse = true;
65bool g_proj_radius = true; 65bool g_proj_radius = true;
66FeatureState g_chams{false, 0}; 66FeatureState g_chams{false, 0};
67ChamsGroup g_chams_hostile{true, 3, {255, 60, 60}, true, 3, {255, 60, 60}}; 67ChamsGroup g_chams_hostile{true, 3, {255, 60, 60, 255}, true, 3,
68ChamsGroup g_chams_neutral{true, 3, {220, 220, 220}, false, 3, {220, 220, 220}}; 68 {255, 60, 60, 255}};
69ChamsGroup g_chams_friendly{true, 3, {60, 255, 60}, false, 3, {60, 255, 60}}; 69ChamsGroup g_chams_neutral{true, 3, {220, 220, 220, 255}, false, 3,
70 {220, 220, 220, 255}};
71ChamsGroup g_chams_friendly{true, 3, {60, 255, 60, 255}, false, 3,
72 {60, 255, 60, 255}};
70bool g_chams_viewmodel = false; 73bool g_chams_viewmodel = false;
71int g_chams_vm_mat = 3; 74int g_chams_vm_mat = 3;
72int g_chams_vm_rgb[3] = {200, 200, 255}; 75int g_chams_vm_rgb[4] = {200, 200, 255, 255};
73bool g_item_health = true; 76bool g_item_health = true;
74bool g_item_ammo = true; 77bool g_item_ammo = true;
75bool g_item_armor = true; 78bool g_item_armor = true;
@@ -244,17 +247,18 @@ void config_save() {
244 fprintf(f, "[chams]\nactive=%d\nbind=%d\n", g_chams.active, g_chams.bind); 247 fprintf(f, "[chams]\nactive=%d\nbind=%d\n", g_chams.active, g_chams.bind);
245 auto save_chams = [&](const char *name, const ChamsGroup &g) { 248 auto save_chams = [&](const char *name, const ChamsGroup &g) {
246 fprintf(f, "[%s]\nvis=%d\nvis_mat=%d\nvis_r=%d\nvis_g=%d\nvis_b=%d\n" 249 fprintf(f, "[%s]\nvis=%d\nvis_mat=%d\nvis_r=%d\nvis_g=%d\nvis_b=%d\n"
247 "inv=%d\ninv_mat=%d\ninv_r=%d\ninv_g=%d\ninv_b=%d\n", 250 "vis_a=%d\ninv=%d\ninv_mat=%d\ninv_r=%d\ninv_g=%d\ninv_b=%d\n"
251 "inv_a=%d\n",
248 name, (int)g.vis_on, g.vis_mat, g.vis_rgb[0], g.vis_rgb[1], 252 name, (int)g.vis_on, g.vis_mat, g.vis_rgb[0], g.vis_rgb[1],
249 g.vis_rgb[2], (int)g.inv_on, g.inv_mat, g.inv_rgb[0], 253 g.vis_rgb[2], g.vis_rgb[3], (int)g.inv_on, g.inv_mat,
250 g.inv_rgb[1], g.inv_rgb[2]); 254 g.inv_rgb[0], g.inv_rgb[1], g.inv_rgb[2], g.inv_rgb[3]);
251 }; 255 };
252 save_chams("chams_hostile", g_chams_hostile); 256 save_chams("chams_hostile", g_chams_hostile);
253 save_chams("chams_neutral", g_chams_neutral); 257 save_chams("chams_neutral", g_chams_neutral);
254 save_chams("chams_friendly", g_chams_friendly); 258 save_chams("chams_friendly", g_chams_friendly);
255 fprintf(f, "[chams_vm]\non=%d\nmat=%d\nr=%d\ng=%d\nb=%d\n", 259 fprintf(f, "[chams_vm]\non=%d\nmat=%d\nr=%d\ng=%d\nb=%d\na=%d\n",
256 (int)g_chams_viewmodel, g_chams_vm_mat, g_chams_vm_rgb[0], 260 (int)g_chams_viewmodel, g_chams_vm_mat, g_chams_vm_rgb[0],
257 g_chams_vm_rgb[1], g_chams_vm_rgb[2]); 261 g_chams_vm_rgb[1], g_chams_vm_rgb[2], g_chams_vm_rgb[3]);
258 fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" 262 fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n"
259 "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" 263 "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n"
260 "usable=%d\n", 264 "usable=%d\n",
@@ -432,11 +436,13 @@ static bool config_load() {
432 if (v > 6) v = 6; 436 if (v > 6) v = 6;
433 g.vis_mat = v; 437 g.vis_mat = v;
434 } else if (strcmp(key, "vis_r") == 0 || strcmp(key, "vis_g") == 0 || 438 } else if (strcmp(key, "vis_r") == 0 || strcmp(key, "vis_g") == 0 ||
435 strcmp(key, "vis_b") == 0) { 439 strcmp(key, "vis_b") == 0 || strcmp(key, "vis_a") == 0) {
436 int v = atoi(val); 440 int v = atoi(val);
437 if (v < 0) v = 0; 441 if (v < 0) v = 0;
438 if (v > 255) v = 255; 442 if (v > 255) v = 255;
439 g.vis_rgb[key[4] == 'r' ? 0 : key[4] == 'g' ? 1 : 2] = v; 443 int idx = key[4] == 'r' ? 0 : key[4] == 'g' ? 1
444 : key[4] == 'b' ? 2 : 3;
445 g.vis_rgb[idx] = v;
440 } else if (strcmp(key, "inv") == 0) 446 } else if (strcmp(key, "inv") == 0)
441 g.inv_on = atoi(val) != 0; 447 g.inv_on = atoi(val) != 0;
442 else if (strcmp(key, "inv_mat") == 0) { 448 else if (strcmp(key, "inv_mat") == 0) {
@@ -445,11 +451,13 @@ static bool config_load() {
445 if (v > 6) v = 6; 451 if (v > 6) v = 6;
446 g.inv_mat = v; 452 g.inv_mat = v;
447 } else if (strcmp(key, "inv_r") == 0 || strcmp(key, "inv_g") == 0 || 453 } else if (strcmp(key, "inv_r") == 0 || strcmp(key, "inv_g") == 0 ||
448 strcmp(key, "inv_b") == 0) { 454 strcmp(key, "inv_b") == 0 || strcmp(key, "inv_a") == 0) {
449 int v = atoi(val); 455 int v = atoi(val);
450 if (v < 0) v = 0; 456 if (v < 0) v = 0;
451 if (v > 255) v = 255; 457 if (v > 255) v = 255;
452 g.inv_rgb[key[4] == 'r' ? 0 : key[4] == 'g' ? 1 : 2] = v; 458 int idx = key[4] == 'r' ? 0 : key[4] == 'g' ? 1
459 : key[4] == 'b' ? 2 : 3;
460 g.inv_rgb[idx] = v;
453 } 461 }
454 } 462 }
455 if (in_chams_vm) { 463 if (in_chams_vm) {
@@ -461,11 +469,13 @@ static bool config_load() {
461 if (v > 6) v = 6; 469 if (v > 6) v = 6;
462 g_chams_vm_mat = v; 470 g_chams_vm_mat = v;
463 } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || 471 } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 ||
464 strcmp(key, "b") == 0) { 472 strcmp(key, "b") == 0 || strcmp(key, "a") == 0) {
465 int v = atoi(val); 473 int v = atoi(val);
466 if (v < 0) v = 0; 474 if (v < 0) v = 0;
467 if (v > 255) v = 255; 475 if (v > 255) v = 255;
468 g_chams_vm_rgb[key[0] == 'r' ? 0 : key[0] == 'g' ? 1 : 2] = v; 476 int idx = key[0] == 'r' ? 0 : key[0] == 'g' ? 1
477 : key[0] == 'b' ? 2 : 3;
478 g_chams_vm_rgb[idx] = v;
469 } 479 }
470 } 480 }
471 if (in_aim) { 481 if (in_aim) {
@@ -693,14 +703,18 @@ void config_reset_defaults() {
693 g_proj_fuse = true; 703 g_proj_fuse = true;
694 g_proj_radius = true; 704 g_proj_radius = true;
695 g_chams = {false, 0}; 705 g_chams = {false, 0};
696 g_chams_hostile = {true, 3, {255, 60, 60}, true, 3, {255, 60, 60}}; 706 g_chams_hostile = {true, 3, {255, 60, 60, 255}, true, 3,
697 g_chams_neutral = {true, 3, {220, 220, 220}, false, 3, {220, 220, 220}}; 707 {255, 60, 60, 255}};
698 g_chams_friendly = {true, 3, {60, 255, 60}, false, 3, {60, 255, 60}}; 708 g_chams_neutral = {true, 3, {220, 220, 220, 255}, false, 3,
709 {220, 220, 220, 255}};
710 g_chams_friendly = {true, 3, {60, 255, 60, 255}, false, 3,
711 {60, 255, 60, 255}};
699 g_chams_viewmodel = false; 712 g_chams_viewmodel = false;
700 g_chams_vm_mat = 3; 713 g_chams_vm_mat = 3;
701 g_chams_vm_rgb[0] = 200; 714 g_chams_vm_rgb[0] = 200;
702 g_chams_vm_rgb[1] = 200; 715 g_chams_vm_rgb[1] = 200;
703 g_chams_vm_rgb[2] = 255; 716 g_chams_vm_rgb[2] = 255;
717 g_chams_vm_rgb[3] = 255;
704 g_item_health = true; 718 g_item_health = true;
705 g_item_ammo = true; 719 g_item_ammo = true;
706 g_item_armor = true; 720 g_item_armor = true;
diff --git a/src/studio.cpp b/src/studio.cpp
index bee6db5..07ebec3 100644
--- a/src/studio.cpp
+++ b/src/studio.cpp
@@ -248,6 +248,15 @@ void chams_color(void *mat, float r, float g, float b) {
248 vfunc<ColorFn>(mat, 28)(mat, r, g, b); 248 vfunc<ColorFn>(mat, 28)(mat, r, g, b);
249} 249}
250 250
251void chams_alpha(void *mat, float a) {
252 using FindVarFn = void *(*)(void *, const char *, bool *, bool);
253 using SetFloatFn = void (*)(void *, float);
254 bool found = false;
255 void *var = vfunc<FindVarFn>(mat, 11)(mat, "$alpha", &found, false);
256 if (var)
257 vfunc<SetFloatFn>(var, 3)(var, a);
258}
259
251void chams_apply(void *mat, bool ignorez, int style, bool force_white, 260void chams_apply(void *mat, bool ignorez, int style, bool force_white,
252 const int *rgb, bool znearer) { 261 const int *rgb, bool znearer) {
253 using FlagFn = void (*)(void *, int, bool); 262 using FlagFn = void (*)(void *, int, bool);
@@ -255,10 +264,12 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white,
255 float g = force_white ? 1.0f : (float)rgb[1] / 255.0f; 264 float g = force_white ? 1.0f : (float)rgb[1] / 255.0f;
256 float b = force_white ? 1.0f : (float)rgb[2] / 255.0f; 265 float b = force_white ? 1.0f : (float)rgb[2] / 255.0f;
257 chams_color(mat, r, g, b); 266 chams_color(mat, r, g, b);
267 chams_alpha(mat, (float)rgb[3] / 255.0f);
258 // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright 268 // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright
259 // (additive white glow), 6 Glow outline (solid base; the wireframe overlay 269 // (additive white glow), 6 Glow outline (solid base; the wireframe overlay
260 // is a second draw with ZNEARER so its contour is not hidden by the base). 270 // is a second draw with ZNEARER so its contour is not hidden by the base).
261 vfunc<FlagFn>(mat, 29)(mat, 1 << 15 /* IGNOREZ */, ignorez); 271 vfunc<FlagFn>(mat, 29)(mat, 1 << 15 /* IGNOREZ */, ignorez);
272 vfunc<FlagFn>(mat, 29)(mat, 1 << 21 /* TRANSLUCENT */, rgb[3] < 255);
262 vfunc<FlagFn>(mat, 29)(mat, 1 << 10 /* ZNEARER */, znearer); 273 vfunc<FlagFn>(mat, 29)(mat, 1 << 10 /* ZNEARER */, znearer);
263 vfunc<FlagFn>(mat, 29)(mat, 1 << 28 /* WIREFRAME */, style == 1); 274 vfunc<FlagFn>(mat, 29)(mat, 1 << 28 /* WIREFRAME */, style == 1);
264 vfunc<FlagFn>(mat, 29)(mat, 1 << 7 /* ADDITIVE */, 275 vfunc<FlagFn>(mat, 29)(mat, 1 << 7 /* ADDITIVE */,
@@ -270,12 +281,14 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white,
270void chams_reset(void *mat) { 281void chams_reset(void *mat) {
271 using FlagFn = void (*)(void *, int, bool); 282 using FlagFn = void (*)(void *, int, bool);
272 vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false); 283 vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false);
284 vfunc<FlagFn>(mat, 29)(mat, 1 << 21, false);
273 vfunc<FlagFn>(mat, 29)(mat, 1 << 10, false); 285 vfunc<FlagFn>(mat, 29)(mat, 1 << 10, false);
274 vfunc<FlagFn>(mat, 29)(mat, 1 << 28, false); 286 vfunc<FlagFn>(mat, 29)(mat, 1 << 28, false);
275 vfunc<FlagFn>(mat, 29)(mat, 1 << 7, false); 287 vfunc<FlagFn>(mat, 29)(mat, 1 << 7, false);
276 vfunc<FlagFn>(mat, 29)(mat, 1 << 12, false); 288 vfunc<FlagFn>(mat, 29)(mat, 1 << 12, false);
277 vfunc<FlagFn>(mat, 29)(mat, 1 << 6, false); 289 vfunc<FlagFn>(mat, 29)(mat, 1 << 6, false);
278 chams_color(mat, 1.0f, 1.0f, 1.0f); 290 chams_color(mat, 1.0f, 1.0f, 1.0f);
291 chams_alpha(mat, 1.0f);
279} 292}
280 293
281void hkdModelExecute(void *self, const void *state, const void *info, 294void hkdModelExecute(void *self, const void *state, const void *info,
@@ -306,20 +319,24 @@ void hkdModelExecute(void *self, const void *state, const void *info,
306 // Fullbright is an additive, self-illuminated version of the 319 // Fullbright is an additive, self-illuminated version of the
307 // chosen colour: lift it toward white so it reads brighter than 320 // chosen colour: lift it toward white so it reads brighter than
308 // the plain additive style. 321 // the plain additive style.
309 int brgb[3]; 322 int brgb[4];
310 const int *use_rgb = rgb; 323 const int *use_rgb = rgb;
311 if (style == 5) { 324 if (style == 5) {
312 for (int i = 0; i < 3; i++) 325 for (int i = 0; i < 3; i++)
313 brgb[i] = rgb[i] + (255 - rgb[i]) / 2; 326 brgb[i] = rgb[i] + (255 - rgb[i]) / 4;
327 brgb[3] = rgb[3];
314 use_rgb = brgb; 328 use_rgb = brgb;
315 } 329 }
316 // Additive blending vanishes against bright backgrounds (sky, 330 // Additive blending vanishes against bright backgrounds (sky,
317 // white walls, strong fog), which made the viewmodel disappear in 331 // white walls, strong fog), which made the viewmodel disappear in
318 // some places. Lay down a dim opaque base first so the first-person 332 // some places. Lay down a faint translucent base so the first-person
319 // model always has a visible silhouette; the additive pass still 333 // model keeps a silhouette on bright backgrounds without turning
320 // adds the glow on top. 334 // into an opaque blob; the additive pass still provides the glow.
321 if (is_vm && (style == 2 || style == 5)) { 335 if (is_vm && (style == 2 || style == 5) && rgb[3] > 0) {
322 int drgb[3] = {rgb[0] / 2, rgb[1] / 2, rgb[2] / 2}; 336 int ba = rgb[3] / 8;
337 if (ba < 16)
338 ba = 16;
339 int drgb[4] = {rgb[0], rgb[1], rgb[2], ba};
323 chams_apply(mat, ignorez, 3, false, drgb, false); 340 chams_apply(mat, ignorez, 3, false, drgb, false);
324 g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); 341 g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */);
325 g_orig_model_exec(self, state, info, bone); 342 g_orig_model_exec(self, state, info, bone);
@@ -333,8 +350,8 @@ void hkdModelExecute(void *self, const void *state, const void *info,
333 // the contour and bias it forward so the base cannot bury it. 350 // the contour and bias it forward so the base cannot bury it.
334 void *wire = g_style_mats[1]; 351 void *wire = g_style_mats[1];
335 if (wire && wire != mat) { 352 if (wire && wire != mat) {
336 int wrgb[3] = {(rgb[0] + 255) / 2, (rgb[1] + 255) / 2, 353 int wrgb[4] = {(rgb[0] + 255) / 2, (rgb[1] + 255) / 2,
337 (rgb[2] + 255) / 2}; 354 (rgb[2] + 255) / 2, rgb[3]};
338 chams_apply(wire, ignorez, 1, false, wrgb, true); 355 chams_apply(wire, ignorez, 1, false, wrgb, true);
339 g_force_override(self, wire, 0); 356 g_force_override(self, wire, 0);
340 g_orig_model_exec(self, state, info, bone); 357 g_orig_model_exec(self, state, info, bone);