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. --- CONTEXT.md | 26 +++++++----- src/gui.cpp | 126 +++++++++++++++++++++++++++++++++++++++++++++------------ src/hl2h.h | 7 ++-- src/menu.cpp | 50 ++++++++++++++--------- src/studio.cpp | 35 +++++++++++----- 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. viewmodel entities (class contains `viewmodel` or model path `.../v_...`) are a fourth group. - Each relationship has independent **visible** and **through-wall** settings: - on/off, style, and colour. The viewmodel has on/off, style and colour only. - The through-wall pass uses `MATERIAL_VAR_IGNOREZ` (1 << 15) through - `IMaterial::SetMaterialVarFlag` (slot 29); colours use `$color`/`$color2` - (IMaterialVar slot 11, SetVecValue 3-float slot 11) and `ColorModulate` - (slot 28). Shared materials are restored after each draw. + on/off, style, colour and alpha. The viewmodel has on/off, style, colour and + alpha only. The through-wall pass uses `MATERIAL_VAR_IGNOREZ` (1 << 15) + through `IMaterial::SetMaterialVarFlag` (slot 29); colours use + `$color`/`$color2` (IMaterialVar slot 11, SetVecValue 3-float slot 11) and + `ColorModulate` (slot 28). Alpha uses `$alpha` (IMaterialVar slot 3, + SetFloatValue) and `MATERIAL_VAR_TRANSLUCENT` (1 << 21) when alpha < 255. + Shared materials are restored after each draw. +- The GUI colour picker (`add_color_alpha`) has an alpha bar, checkerboard + preview and `#RRGGBBAA` readout; plain `add_color` keeps the old opaque + three-component behaviour. Chams colours are four ints (r, g, b, a). - Styles are built from `debug/debugtranslucentsinglecolor` (UnlitGeneric, `$color`) and material flags: 0 Flat (solid + FLAT), 1 Wireframe (`debug/debugwireframe`), 2 Additive @@ -310,12 +315,13 @@ vocabulary / decisions. - Viewmodels are collated by the engine as `RENDER_GROUP_VIEW_MODEL_OPAQUE`, so an additive forced material can wash out against a bright sky or wall (and the engine may not treat the draw as translucent). Additive styles on - the viewmodel therefore draw a dim opaque solid base first, then the - additive pass; the first-person model never disappears. + the viewmodel therefore draw a faint translucent base (alpha/8) first, then + the additive pass, so the model keeps a silhouette on bright backgrounds + without becoming an opaque blob. - Feature is off by default. Config sections: `[chams]` (active/bind), - `[chams_hostile|neutral|friendly]` (`vis`, `vis_mat`, `vis_r/g/b`, `inv`, - `inv_mat`, `inv_r/g/b`) and `[chams_vm]` (`on`, `mat`, `r/g/b`). The GUI has - a Hostile/Neutral/Friendly section each, plus Viewmodel. + `[chams_hostile|neutral|friendly]` (`vis`, `vis_mat`, `vis_r/g/b/a`, `inv`, + `inv_mat`, `inv_r/g/b/a`) and `[chams_vm]` (`on`, `mat`, `r/g/b/a`). The GUI + has a Hostile/Neutral/Friendly section each, plus Viewmodel. - This is a material override, not stencil outlines. ## 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 { 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); 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; struct ChamsGroup { bool vis_on; int vis_mat; - int vis_rgb[3]; + int vis_rgb[4]; // r, g, b, a (0..255) bool inv_on; int inv_mat; - int inv_rgb[3]; + int inv_rgb[4]; }; extern ChamsGroup g_chams_hostile, g_chams_neutral, g_chams_friendly; extern bool g_chams_viewmodel; extern int g_chams_vm_mat; -extern int g_chams_vm_rgb[3];extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; +extern int g_chams_vm_rgb[4]; +extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; extern bool g_fog_skybox, g_fog_disable; extern 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 bool g_proj_fuse = true; bool g_proj_radius = true; FeatureState g_chams{false, 0}; -ChamsGroup g_chams_hostile{true, 3, {255, 60, 60}, true, 3, {255, 60, 60}}; -ChamsGroup g_chams_neutral{true, 3, {220, 220, 220}, false, 3, {220, 220, 220}}; -ChamsGroup g_chams_friendly{true, 3, {60, 255, 60}, false, 3, {60, 255, 60}}; +ChamsGroup g_chams_hostile{true, 3, {255, 60, 60, 255}, true, 3, + {255, 60, 60, 255}}; +ChamsGroup g_chams_neutral{true, 3, {220, 220, 220, 255}, false, 3, + {220, 220, 220, 255}}; +ChamsGroup g_chams_friendly{true, 3, {60, 255, 60, 255}, false, 3, + {60, 255, 60, 255}}; bool g_chams_viewmodel = false; int g_chams_vm_mat = 3; -int g_chams_vm_rgb[3] = {200, 200, 255}; +int g_chams_vm_rgb[4] = {200, 200, 255, 255}; bool g_item_health = true; bool g_item_ammo = true; bool g_item_armor = true; @@ -244,17 +247,18 @@ void config_save() { fprintf(f, "[chams]\nactive=%d\nbind=%d\n", g_chams.active, g_chams.bind); auto save_chams = [&](const char *name, const ChamsGroup &g) { fprintf(f, "[%s]\nvis=%d\nvis_mat=%d\nvis_r=%d\nvis_g=%d\nvis_b=%d\n" - "inv=%d\ninv_mat=%d\ninv_r=%d\ninv_g=%d\ninv_b=%d\n", + "vis_a=%d\ninv=%d\ninv_mat=%d\ninv_r=%d\ninv_g=%d\ninv_b=%d\n" + "inv_a=%d\n", name, (int)g.vis_on, g.vis_mat, g.vis_rgb[0], g.vis_rgb[1], - g.vis_rgb[2], (int)g.inv_on, g.inv_mat, g.inv_rgb[0], - g.inv_rgb[1], g.inv_rgb[2]); + g.vis_rgb[2], g.vis_rgb[3], (int)g.inv_on, g.inv_mat, + g.inv_rgb[0], g.inv_rgb[1], g.inv_rgb[2], g.inv_rgb[3]); }; save_chams("chams_hostile", g_chams_hostile); save_chams("chams_neutral", g_chams_neutral); save_chams("chams_friendly", g_chams_friendly); - fprintf(f, "[chams_vm]\non=%d\nmat=%d\nr=%d\ng=%d\nb=%d\n", + fprintf(f, "[chams_vm]\non=%d\nmat=%d\nr=%d\ng=%d\nb=%d\na=%d\n", (int)g_chams_viewmodel, g_chams_vm_mat, g_chams_vm_rgb[0], - g_chams_vm_rgb[1], g_chams_vm_rgb[2]); + g_chams_vm_rgb[1], g_chams_vm_rgb[2], g_chams_vm_rgb[3]); fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" "usable=%d\n", @@ -432,11 +436,13 @@ static bool config_load() { if (v > 6) v = 6; g.vis_mat = v; } else if (strcmp(key, "vis_r") == 0 || strcmp(key, "vis_g") == 0 || - strcmp(key, "vis_b") == 0) { + strcmp(key, "vis_b") == 0 || strcmp(key, "vis_a") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; - g.vis_rgb[key[4] == 'r' ? 0 : key[4] == 'g' ? 1 : 2] = v; + int idx = key[4] == 'r' ? 0 : key[4] == 'g' ? 1 + : key[4] == 'b' ? 2 : 3; + g.vis_rgb[idx] = v; } else if (strcmp(key, "inv") == 0) g.inv_on = atoi(val) != 0; else if (strcmp(key, "inv_mat") == 0) { @@ -445,11 +451,13 @@ static bool config_load() { if (v > 6) v = 6; g.inv_mat = v; } else if (strcmp(key, "inv_r") == 0 || strcmp(key, "inv_g") == 0 || - strcmp(key, "inv_b") == 0) { + strcmp(key, "inv_b") == 0 || strcmp(key, "inv_a") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; - g.inv_rgb[key[4] == 'r' ? 0 : key[4] == 'g' ? 1 : 2] = v; + int idx = key[4] == 'r' ? 0 : key[4] == 'g' ? 1 + : key[4] == 'b' ? 2 : 3; + g.inv_rgb[idx] = v; } } if (in_chams_vm) { @@ -461,11 +469,13 @@ static bool config_load() { if (v > 6) v = 6; g_chams_vm_mat = v; } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || - strcmp(key, "b") == 0) { + strcmp(key, "b") == 0 || strcmp(key, "a") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; - g_chams_vm_rgb[key[0] == 'r' ? 0 : key[0] == 'g' ? 1 : 2] = v; + int idx = key[0] == 'r' ? 0 : key[0] == 'g' ? 1 + : key[0] == 'b' ? 2 : 3; + g_chams_vm_rgb[idx] = v; } } if (in_aim) { @@ -693,14 +703,18 @@ void config_reset_defaults() { g_proj_fuse = true; g_proj_radius = true; g_chams = {false, 0}; - g_chams_hostile = {true, 3, {255, 60, 60}, true, 3, {255, 60, 60}}; - g_chams_neutral = {true, 3, {220, 220, 220}, false, 3, {220, 220, 220}}; - g_chams_friendly = {true, 3, {60, 255, 60}, false, 3, {60, 255, 60}}; + g_chams_hostile = {true, 3, {255, 60, 60, 255}, true, 3, + {255, 60, 60, 255}}; + g_chams_neutral = {true, 3, {220, 220, 220, 255}, false, 3, + {220, 220, 220, 255}}; + g_chams_friendly = {true, 3, {60, 255, 60, 255}, false, 3, + {60, 255, 60, 255}}; g_chams_viewmodel = false; g_chams_vm_mat = 3; g_chams_vm_rgb[0] = 200; g_chams_vm_rgb[1] = 200; g_chams_vm_rgb[2] = 255; + g_chams_vm_rgb[3] = 255; g_item_health = true; g_item_ammo = true; 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) { vfunc(mat, 28)(mat, r, g, b); } +void chams_alpha(void *mat, float a) { + using FindVarFn = void *(*)(void *, const char *, bool *, bool); + using SetFloatFn = void (*)(void *, float); + bool found = false; + void *var = vfunc(mat, 11)(mat, "$alpha", &found, false); + if (var) + vfunc(var, 3)(var, a); +} + void chams_apply(void *mat, bool ignorez, int style, bool force_white, const int *rgb, bool znearer) { using FlagFn = void (*)(void *, int, bool); @@ -255,10 +264,12 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white, float g = force_white ? 1.0f : (float)rgb[1] / 255.0f; float b = force_white ? 1.0f : (float)rgb[2] / 255.0f; chams_color(mat, r, g, b); + chams_alpha(mat, (float)rgb[3] / 255.0f); // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright // (additive white glow), 6 Glow outline (solid base; the wireframe overlay // is a second draw with ZNEARER so its contour is not hidden by the base). vfunc(mat, 29)(mat, 1 << 15 /* IGNOREZ */, ignorez); + vfunc(mat, 29)(mat, 1 << 21 /* TRANSLUCENT */, rgb[3] < 255); vfunc(mat, 29)(mat, 1 << 10 /* ZNEARER */, znearer); vfunc(mat, 29)(mat, 1 << 28 /* WIREFRAME */, style == 1); vfunc(mat, 29)(mat, 1 << 7 /* ADDITIVE */, @@ -270,12 +281,14 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white, void chams_reset(void *mat) { using FlagFn = void (*)(void *, int, bool); vfunc(mat, 29)(mat, 1 << 15, false); + vfunc(mat, 29)(mat, 1 << 21, false); vfunc(mat, 29)(mat, 1 << 10, false); vfunc(mat, 29)(mat, 1 << 28, false); vfunc(mat, 29)(mat, 1 << 7, false); vfunc(mat, 29)(mat, 1 << 12, false); vfunc(mat, 29)(mat, 1 << 6, false); chams_color(mat, 1.0f, 1.0f, 1.0f); + chams_alpha(mat, 1.0f); } void hkdModelExecute(void *self, const void *state, const void *info, @@ -306,20 +319,24 @@ void hkdModelExecute(void *self, const void *state, const void *info, // Fullbright is an additive, self-illuminated version of the // chosen colour: lift it toward white so it reads brighter than // the plain additive style. - int brgb[3]; + int brgb[4]; const int *use_rgb = rgb; if (style == 5) { for (int i = 0; i < 3; i++) - brgb[i] = rgb[i] + (255 - rgb[i]) / 2; + brgb[i] = rgb[i] + (255 - rgb[i]) / 4; + brgb[3] = rgb[3]; use_rgb = brgb; } // Additive blending vanishes against bright backgrounds (sky, // white walls, strong fog), which made the viewmodel disappear in - // some places. Lay down a dim opaque base first so the first-person - // model always has a visible silhouette; the additive pass still - // adds the glow on top. - if (is_vm && (style == 2 || style == 5)) { - int drgb[3] = {rgb[0] / 2, rgb[1] / 2, rgb[2] / 2}; + // some places. Lay down a faint translucent base so the first-person + // model keeps a silhouette on bright backgrounds without turning + // into an opaque blob; the additive pass still provides the glow. + if (is_vm && (style == 2 || style == 5) && rgb[3] > 0) { + int ba = rgb[3] / 8; + if (ba < 16) + ba = 16; + int drgb[4] = {rgb[0], rgb[1], rgb[2], ba}; chams_apply(mat, ignorez, 3, false, drgb, false); g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); g_orig_model_exec(self, state, info, bone); @@ -333,8 +350,8 @@ void hkdModelExecute(void *self, const void *state, const void *info, // the contour and bias it forward so the base cannot bury it. void *wire = g_style_mats[1]; if (wire && wire != mat) { - int wrgb[3] = {(rgb[0] + 255) / 2, (rgb[1] + 255) / 2, - (rgb[2] + 255) / 2}; + int wrgb[4] = {(rgb[0] + 255) / 2, (rgb[1] + 255) / 2, + (rgb[2] + 255) / 2, rgb[3]}; chams_apply(wire, ignorez, 1, false, wrgb, true); g_force_override(self, wire, 0); g_orig_model_exec(self, state, info, bone); -- cgit v1.2.3