From b3003583a1a66f5ae2eae63763f285635b43d58f Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 18:08:07 +0100 Subject: Chams: material styles and viewmodel support - Material selector 0..4: debugwhite, debug wireframe (ignorez), vgui white_additive, debugtranslucentsinglecolor, vgui/white; cached per index with a debugwhite fallback. - Add "Viewmodel (hands/weapon)" toggle; viewmodel entities (class contains viewmodel or model path /v_...) get a normal-depth chams pass. - [chams] gains viewmodel= and material=. --- CONTEXT.md | 17 +++++++++++------ src/esp.cpp | 14 ++++++++++++++ src/gui.cpp | 6 ++++++ src/hl2h.h | 3 +++ src/menu.cpp | 21 +++++++++++++++++---- src/studio.cpp | 58 ++++++++++++++++++++++++++++++++++++++-------------------- 6 files changed, 89 insertions(+), 30 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 60a5a91..c95095b 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -293,12 +293,17 @@ vocabulary / decisions. materials ignore ColorModulate. The shared material's flag and colour are restored to (false, white) after the draw. - Material lookup walks the `IMaterialSystem` list (`world_find_material`), with - `FindMaterial` (slot 71) as the fallback. Candidates, in order: - `models/debug/debugwhite{2,3}`, `vgui/white`, - `debug/debugtranslucentsinglecolor`, `debug/debugwireframevertexcolorignorez`. - The chosen material name is logged. -- Feature is off by default; `[chams] active/bind/walls/r/g/b`, with a - "Through walls" toggle and colour picker in the Visuals card. + `FindMaterial` (slot 71) as the fallback. The style index selects: + 0 `models/debug/debugwhite`, 1 `debug/debugwireframevertexcolorignorez`, + 2 `vgui/white_additive`, 3 `debug/debugtranslucentsinglecolor`, + 4 `vgui/white`; if the requested style is unavailable it falls back to the + first available debugwhite candidate. The chosen material name is logged. +- `Viewmodel (hands/weapon)` also applies the override to entities whose class + contains `viewmodel` or whose model path is `.../v_...`. The viewmodel gets + only the normal-depth pass (no ignorez). +- Feature is off by default; `[chams] active/bind/walls/viewmodel/material/r/g/b`, + with Through walls, Viewmodel, Material (0..4) and colour in the Visuals + card. - This is a flat material override, not stencil outlines; depth handling is the simple ignorez/normal two-pass. diff --git a/src/esp.cpp b/src/esp.cpp index b50dd87..1631df1 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -173,6 +173,20 @@ bool esp_is_npc_index(int idx) { return is_npc_class(entity_class_name(net, nullptr)); } +bool esp_is_viewmodel_index(int idx) { + if (!g_iface.entitylist || idx <= 0 || idx > 8192) + return false; + void *ent = entitylist_get(idx); + if (!ent) + return false; + void *net = entity_networkable(idx); + const char *cls = net ? entity_class_name(net, nullptr) : nullptr; + if (cls && strcasestr(cls, "viewmodel")) + return true; + const char *model = entity_model_name(ent); + return model && (strstr(model, "/v_") || strstr(model, "\\v_")); +} + static bool model_is_friendly(const char *m) { static const char *patterns[] = { "citizen", "humans/", "male_", "female_", "barney", "alyx", diff --git a/src/gui.cpp b/src/gui.cpp index a38f537..4a949f0 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -428,6 +428,12 @@ static void build_cards() { add_toggle(chams, "Through walls", "Draw a second pass with the depth test disabled.", &g_chams_walls); + add_toggle(chams, "Viewmodel (hands/weapon)", + "Apply chams to the first-person hands and weapon too.", + &g_chams_viewmodel); + add_slider(chams, "Material", + "0 Flat, 1 Wireframe, 2 Additive glow, 3 Solid, 4 White.", 0, 4, + &g_chams_mat_index); add_color(chams, "Colour", "Chams colour.", g_chams_rgb); int view = add_card(2, nullptr, "Camera / display", diff --git a/src/hl2h.h b/src/hl2h.h index 55fac0d..1be1bb1 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -231,6 +231,8 @@ extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, g_world_tint, g_fog, g_skybox, g_projectiles, g_chams; extern bool g_proj_fuse, g_proj_radius; extern bool g_chams_walls; +extern bool g_chams_viewmodel; +extern int g_chams_mat_index; extern int g_chams_rgb[3]; 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]; @@ -334,6 +336,7 @@ void aimbot_reset_lock(); void silentaim_reset_lock(); void *entitylist_get(int idx); bool esp_is_npc_index(int idx); +bool esp_is_viewmodel_index(int idx); void *world_find_material(const char *name); void chams_init(); int engine_get_local_player(); diff --git a/src/menu.cpp b/src/menu.cpp index 77eb35f..4f22d39 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -65,6 +65,8 @@ bool g_proj_fuse = true; bool g_proj_radius = true; FeatureState g_chams{false, 0}; // flat model material, optional through walls bool g_chams_walls = true; +bool g_chams_viewmodel = false; +int g_chams_mat_index = 0; int g_chams_rgb[3] = {255, 60, 60}; bool g_item_health = true; bool g_item_ammo = true; @@ -237,8 +239,10 @@ void config_save() { fprintf(f, "[projectiles]\nactive=%d\nbind=%d\nfuse=%d\nradius=%d\n", g_projectiles.active, g_projectiles.bind, (int)g_proj_fuse, (int)g_proj_radius); - fprintf(f, "[chams]\nactive=%d\nbind=%d\nwalls=%d\nr=%d\ng=%d\nb=%d\n", - g_chams.active, g_chams.bind, (int)g_chams_walls, g_chams_rgb[0], + fprintf(f, "[chams]\nactive=%d\nbind=%d\nwalls=%d\nviewmodel=%d\n" + "material=%d\nr=%d\ng=%d\nb=%d\n", + g_chams.active, g_chams.bind, (int)g_chams_walls, + (int)g_chams_viewmodel, g_chams_mat_index, g_chams_rgb[0], g_chams_rgb[1], g_chams_rgb[2]); fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" @@ -393,8 +397,15 @@ static bool config_load() { if (in_chams) { if (strcmp(key, "walls") == 0) g_chams_walls = atoi(val) != 0; - else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || - strcmp(key, "b") == 0) { + else if (strcmp(key, "viewmodel") == 0) + g_chams_viewmodel = atoi(val) != 0; + else if (strcmp(key, "material") == 0) { + int v = atoi(val); + if (v < 0) v = 0; + if (v > 4) v = 4; + g_chams_mat_index = v; + } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || + strcmp(key, "b") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; @@ -627,6 +638,8 @@ void config_reset_defaults() { g_proj_radius = true; g_chams = {false, 0}; g_chams_walls = true; + g_chams_viewmodel = false; + g_chams_mat_index = 0; g_chams_rgb[0] = 255; g_chams_rgb[1] = 60; g_chams_rgb[2] = 60; diff --git a/src/studio.cpp b/src/studio.cpp index a1aae12..e667c4b 100644 --- a/src/studio.cpp +++ b/src/studio.cpp @@ -197,15 +197,29 @@ void *g_modelrender = nullptr; ModelExecFn g_orig_model_exec = nullptr; ForceOverrideFn g_force_override = nullptr; void *g_chams_mat = nullptr; -bool g_chams_mat_tried = false; +int g_chams_mat_idx = -1; + +const char *chams_material_name(int idx) { + switch (idx) { + case 1: return "debug/debugwireframevertexcolorignorez"; + case 2: return "vgui/white_additive"; + case 3: return "debug/debugtranslucentsinglecolor"; + case 4: return "vgui/white"; + default: return "models/debug/debugwhite"; + } +} void *chams_material() { - if (g_chams_mat_tried) + int idx = g_chams_mat_index; + if (idx < 0) idx = 0; + if (idx > 4) idx = 4; + if (g_chams_mat && g_chams_mat_idx == idx) return g_chams_mat; - g_chams_mat_tried = true; - g_chams_mat = world_find_material("models/debug/debugwhite"); + g_chams_mat = world_find_material(chams_material_name(idx)); if (!g_chams_mat) - logf("chams: no usable material"); + g_chams_mat = world_find_material("models/debug/debugwhite"); + g_chams_mat_idx = idx; + logf("chams: style %d -> %p", idx, g_chams_mat); return g_chams_mat; } @@ -239,23 +253,27 @@ void hkdModelExecute(void *self, const void *state, const void *info, if (feature_active(FEAT_CHAMS) && info && g_force_override) { // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit. int idx = *(const int *)((const char *)info + 0x30); - if (idx > 0 && idx <= 8192 && esp_is_npc_index(idx)) { - void *mat = chams_material(); - if (mat) { - if (g_chams_walls) { - chams_apply(mat, true); - g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); + if (idx > 0 && idx <= 8192) { + bool is_npc = esp_is_npc_index(idx); + bool is_vm = g_chams_viewmodel && esp_is_viewmodel_index(idx); + if (is_npc || is_vm) { + void *mat = chams_material(); + if (mat) { + if (g_chams_walls && !is_vm) { + chams_apply(mat, true); + g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); + g_orig_model_exec(self, state, info, bone); + } + chams_apply(mat, false); + g_force_override(self, mat, 0); g_orig_model_exec(self, state, info, bone); + g_force_override(self, nullptr, 0); + // debugwhite is shared: restore colour and depth behaviour. + using FlagFn = void (*)(void *, int, bool); + vfunc(mat, 29)(mat, 1 << 15, false); + chams_color(mat, 1.0f, 1.0f, 1.0f); + return; } - chams_apply(mat, false); - g_force_override(self, mat, 0); - g_orig_model_exec(self, state, info, bone); - g_force_override(self, nullptr, 0); - // debugwhite is shared: restore colour and depth behaviour. - using FlagFn = void (*)(void *, int, bool); - vfunc(mat, 29)(mat, 1 << 15, false); - chams_color(mat, 1.0f, 1.0f, 1.0f); - return; } } } -- cgit v1.2.3