summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 18:08:07 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 18:08:07 +0100
commitb3003583a1a66f5ae2eae63763f285635b43d58f (patch)
treed4d2e778a2265652befcc86fbaa8bf1f4fcb30c3
parentChams: quiet material lookup, accept any fallback by name (diff)
downloadhl2h-b3003583a1a66f5ae2eae63763f285635b43d58f.tar.gz
hl2h-b3003583a1a66f5ae2eae63763f285635b43d58f.zip
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=.
-rw-r--r--CONTEXT.md17
-rw-r--r--src/esp.cpp14
-rw-r--r--src/gui.cpp6
-rw-r--r--src/hl2h.h3
-rw-r--r--src/menu.cpp21
-rw-r--r--src/studio.cpp58
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.
293 materials ignore ColorModulate. The shared material's flag and colour are 293 materials ignore ColorModulate. The shared material's flag and colour are
294 restored to (false, white) after the draw. 294 restored to (false, white) after the draw.
295- Material lookup walks the `IMaterialSystem` list (`world_find_material`), with 295- Material lookup walks the `IMaterialSystem` list (`world_find_material`), with
296 `FindMaterial` (slot 71) as the fallback. Candidates, in order: 296 `FindMaterial` (slot 71) as the fallback. The style index selects:
297 `models/debug/debugwhite{2,3}`, `vgui/white`, 297 0 `models/debug/debugwhite`, 1 `debug/debugwireframevertexcolorignorez`,
298 `debug/debugtranslucentsinglecolor`, `debug/debugwireframevertexcolorignorez`. 298 2 `vgui/white_additive`, 3 `debug/debugtranslucentsinglecolor`,
299 The chosen material name is logged. 299 4 `vgui/white`; if the requested style is unavailable it falls back to the
300- Feature is off by default; `[chams] active/bind/walls/r/g/b`, with a 300 first available debugwhite candidate. The chosen material name is logged.
301 "Through walls" toggle and colour picker in the Visuals card. 301- `Viewmodel (hands/weapon)` also applies the override to entities whose class
302 contains `viewmodel` or whose model path is `.../v_...`. The viewmodel gets
303 only the normal-depth pass (no ignorez).
304- Feature is off by default; `[chams] active/bind/walls/viewmodel/material/r/g/b`,
305 with Through walls, Viewmodel, Material (0..4) and colour in the Visuals
306 card.
302- This is a flat material override, not stencil outlines; depth handling is 307- This is a flat material override, not stencil outlines; depth handling is
303 the simple ignorez/normal two-pass. 308 the simple ignorez/normal two-pass.
304 309
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) {
173 return is_npc_class(entity_class_name(net, nullptr)); 173 return is_npc_class(entity_class_name(net, nullptr));
174} 174}
175 175
176bool esp_is_viewmodel_index(int idx) {
177 if (!g_iface.entitylist || idx <= 0 || idx > 8192)
178 return false;
179 void *ent = entitylist_get(idx);
180 if (!ent)
181 return false;
182 void *net = entity_networkable(idx);
183 const char *cls = net ? entity_class_name(net, nullptr) : nullptr;
184 if (cls && strcasestr(cls, "viewmodel"))
185 return true;
186 const char *model = entity_model_name(ent);
187 return model && (strstr(model, "/v_") || strstr(model, "\\v_"));
188}
189
176static bool model_is_friendly(const char *m) { 190static bool model_is_friendly(const char *m) {
177 static const char *patterns[] = { 191 static const char *patterns[] = {
178 "citizen", "humans/", "male_", "female_", "barney", "alyx", 192 "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() {
428 add_toggle(chams, "Through walls", 428 add_toggle(chams, "Through walls",
429 "Draw a second pass with the depth test disabled.", 429 "Draw a second pass with the depth test disabled.",
430 &g_chams_walls); 430 &g_chams_walls);
431 add_toggle(chams, "Viewmodel (hands/weapon)",
432 "Apply chams to the first-person hands and weapon too.",
433 &g_chams_viewmodel);
434 add_slider(chams, "Material",
435 "0 Flat, 1 Wireframe, 2 Additive glow, 3 Solid, 4 White.", 0, 4,
436 &g_chams_mat_index);
431 add_color(chams, "Colour", "Chams colour.", g_chams_rgb); 437 add_color(chams, "Colour", "Chams colour.", g_chams_rgb);
432 438
433 int view = add_card(2, nullptr, "Camera / display", 439 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,
231 g_world_tint, g_fog, g_skybox, g_projectiles, g_chams; 231 g_world_tint, g_fog, g_skybox, g_projectiles, g_chams;
232extern bool g_proj_fuse, g_proj_radius; 232extern bool g_proj_fuse, g_proj_radius;
233extern bool g_chams_walls; 233extern bool g_chams_walls;
234extern bool g_chams_viewmodel;
235extern int g_chams_mat_index;
234extern int g_chams_rgb[3]; 236extern int g_chams_rgb[3];
235extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; 237extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on;
236extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; 238extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3];
@@ -334,6 +336,7 @@ void aimbot_reset_lock();
334void silentaim_reset_lock(); 336void silentaim_reset_lock();
335void *entitylist_get(int idx); 337void *entitylist_get(int idx);
336bool esp_is_npc_index(int idx); 338bool esp_is_npc_index(int idx);
339bool esp_is_viewmodel_index(int idx);
337void *world_find_material(const char *name); 340void *world_find_material(const char *name);
338void chams_init(); 341void chams_init();
339int engine_get_local_player(); 342int 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;
65bool g_proj_radius = true; 65bool g_proj_radius = true;
66FeatureState g_chams{false, 0}; // flat model material, optional through walls 66FeatureState g_chams{false, 0}; // flat model material, optional through walls
67bool g_chams_walls = true; 67bool g_chams_walls = true;
68bool g_chams_viewmodel = false;
69int g_chams_mat_index = 0;
68int g_chams_rgb[3] = {255, 60, 60}; 70int g_chams_rgb[3] = {255, 60, 60};
69bool g_item_health = true; 71bool g_item_health = true;
70bool g_item_ammo = true; 72bool g_item_ammo = true;
@@ -237,8 +239,10 @@ void config_save() {
237 fprintf(f, "[projectiles]\nactive=%d\nbind=%d\nfuse=%d\nradius=%d\n", 239 fprintf(f, "[projectiles]\nactive=%d\nbind=%d\nfuse=%d\nradius=%d\n",
238 g_projectiles.active, g_projectiles.bind, (int)g_proj_fuse, 240 g_projectiles.active, g_projectiles.bind, (int)g_proj_fuse,
239 (int)g_proj_radius); 241 (int)g_proj_radius);
240 fprintf(f, "[chams]\nactive=%d\nbind=%d\nwalls=%d\nr=%d\ng=%d\nb=%d\n", 242 fprintf(f, "[chams]\nactive=%d\nbind=%d\nwalls=%d\nviewmodel=%d\n"
241 g_chams.active, g_chams.bind, (int)g_chams_walls, g_chams_rgb[0], 243 "material=%d\nr=%d\ng=%d\nb=%d\n",
244 g_chams.active, g_chams.bind, (int)g_chams_walls,
245 (int)g_chams_viewmodel, g_chams_mat_index, g_chams_rgb[0],
242 g_chams_rgb[1], g_chams_rgb[2]); 246 g_chams_rgb[1], g_chams_rgb[2]);
243 fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" 247 fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n"
244 "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" 248 "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n"
@@ -393,8 +397,15 @@ static bool config_load() {
393 if (in_chams) { 397 if (in_chams) {
394 if (strcmp(key, "walls") == 0) 398 if (strcmp(key, "walls") == 0)
395 g_chams_walls = atoi(val) != 0; 399 g_chams_walls = atoi(val) != 0;
396 else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || 400 else if (strcmp(key, "viewmodel") == 0)
397 strcmp(key, "b") == 0) { 401 g_chams_viewmodel = atoi(val) != 0;
402 else if (strcmp(key, "material") == 0) {
403 int v = atoi(val);
404 if (v < 0) v = 0;
405 if (v > 4) v = 4;
406 g_chams_mat_index = v;
407 } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 ||
408 strcmp(key, "b") == 0) {
398 int v = atoi(val); 409 int v = atoi(val);
399 if (v < 0) v = 0; 410 if (v < 0) v = 0;
400 if (v > 255) v = 255; 411 if (v > 255) v = 255;
@@ -627,6 +638,8 @@ void config_reset_defaults() {
627 g_proj_radius = true; 638 g_proj_radius = true;
628 g_chams = {false, 0}; 639 g_chams = {false, 0};
629 g_chams_walls = true; 640 g_chams_walls = true;
641 g_chams_viewmodel = false;
642 g_chams_mat_index = 0;
630 g_chams_rgb[0] = 255; 643 g_chams_rgb[0] = 255;
631 g_chams_rgb[1] = 60; 644 g_chams_rgb[1] = 60;
632 g_chams_rgb[2] = 60; 645 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;
197ModelExecFn g_orig_model_exec = nullptr; 197ModelExecFn g_orig_model_exec = nullptr;
198ForceOverrideFn g_force_override = nullptr; 198ForceOverrideFn g_force_override = nullptr;
199void *g_chams_mat = nullptr; 199void *g_chams_mat = nullptr;
200bool g_chams_mat_tried = false; 200int g_chams_mat_idx = -1;
201
202const char *chams_material_name(int idx) {
203 switch (idx) {
204 case 1: return "debug/debugwireframevertexcolorignorez";
205 case 2: return "vgui/white_additive";
206 case 3: return "debug/debugtranslucentsinglecolor";
207 case 4: return "vgui/white";
208 default: return "models/debug/debugwhite";
209 }
210}
201 211
202void *chams_material() { 212void *chams_material() {
203 if (g_chams_mat_tried) 213 int idx = g_chams_mat_index;
214 if (idx < 0) idx = 0;
215 if (idx > 4) idx = 4;
216 if (g_chams_mat && g_chams_mat_idx == idx)
204 return g_chams_mat; 217 return g_chams_mat;
205 g_chams_mat_tried = true; 218 g_chams_mat = world_find_material(chams_material_name(idx));
206 g_chams_mat = world_find_material("models/debug/debugwhite");
207 if (!g_chams_mat) 219 if (!g_chams_mat)
208 logf("chams: no usable material"); 220 g_chams_mat = world_find_material("models/debug/debugwhite");
221 g_chams_mat_idx = idx;
222 logf("chams: style %d -> %p", idx, g_chams_mat);
209 return g_chams_mat; 223 return g_chams_mat;
210} 224}
211 225
@@ -239,23 +253,27 @@ void hkdModelExecute(void *self, const void *state, const void *info,
239 if (feature_active(FEAT_CHAMS) && info && g_force_override) { 253 if (feature_active(FEAT_CHAMS) && info && g_force_override) {
240 // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit. 254 // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit.
241 int idx = *(const int *)((const char *)info + 0x30); 255 int idx = *(const int *)((const char *)info + 0x30);
242 if (idx > 0 && idx <= 8192 && esp_is_npc_index(idx)) { 256 if (idx > 0 && idx <= 8192) {
243 void *mat = chams_material(); 257 bool is_npc = esp_is_npc_index(idx);
244 if (mat) { 258 bool is_vm = g_chams_viewmodel && esp_is_viewmodel_index(idx);
245 if (g_chams_walls) { 259 if (is_npc || is_vm) {
246 chams_apply(mat, true); 260 void *mat = chams_material();
247 g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); 261 if (mat) {
262 if (g_chams_walls && !is_vm) {
263 chams_apply(mat, true);
264 g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */);
265 g_orig_model_exec(self, state, info, bone);
266 }
267 chams_apply(mat, false);
268 g_force_override(self, mat, 0);
248 g_orig_model_exec(self, state, info, bone); 269 g_orig_model_exec(self, state, info, bone);
270 g_force_override(self, nullptr, 0);
271 // debugwhite is shared: restore colour and depth behaviour.
272 using FlagFn = void (*)(void *, int, bool);
273 vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false);
274 chams_color(mat, 1.0f, 1.0f, 1.0f);
275 return;
249 } 276 }
250 chams_apply(mat, false);
251 g_force_override(self, mat, 0);
252 g_orig_model_exec(self, state, info, bone);
253 g_force_override(self, nullptr, 0);
254 // debugwhite is shared: restore colour and depth behaviour.
255 using FlagFn = void (*)(void *, int, bool);
256 vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false);
257 chams_color(mat, 1.0f, 1.0f, 1.0f);
258 return;
259 } 277 }
260 } 278 }
261 } 279 }