From 5ddc9eb9d02ddc84e4c3a45bdcb2941513fb0346 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 18:58:13 +0100 Subject: Chams: per-relationship, visible/invisible styles and viewmodel - Hostile/neutral/friendly each have independent visible and through-wall settings: enable, style and colour. Viewmodel has its own enable/style/colour. - Styles: Flat, Wireframe, Additive, Solid, White, Fullbright, Glow outline. Additive uses the model solid material + ADDITIVE flag (the VGUI additive was invisible on NPCs). White/fullbright are solid-based, so the viewmodel no longer disappears/checkerboards. Glow outline adds a wireframe contour pass for depth. - Config: [chams] active/bind, [chams_hostile|neutral|friendly] vis/inv settings, [chams_vm]. GUI sections for each group. --- src/studio.cpp | 94 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 37 deletions(-) (limited to 'src/studio.cpp') diff --git a/src/studio.cpp b/src/studio.cpp index b428a26..803b6b2 100644 --- a/src/studio.cpp +++ b/src/studio.cpp @@ -197,25 +197,25 @@ using ForceOverrideFn = void (*)(void *, void *, int); void *g_modelrender = nullptr; ModelExecFn g_orig_model_exec = nullptr; ForceOverrideFn g_force_override = nullptr; -void *g_style_mats[6] = {}; +void *g_style_mats[7] = {}; void *viewmodel_material(); const char *chams_material_name(int idx) { switch (idx) { case 1: return "debug/debugwireframe"; - case 2: return "vgui/white_additive"; - case 3: return "debug/debugtranslucentsinglecolor"; - case 4: return "debug/debugtranslucentvertexcolor"; - case 5: return "debug/debugmrmfullbright2"; + case 2: + case 3: + case 4: + case 5: + case 6: return "debug/debugtranslucentsinglecolor"; default: return "vgui/white"; // models/debug/debugwhite is EP2-only } } -void *chams_material() { - int idx = g_chams_mat_index; +void *style_material(int idx) { if (idx < 0) idx = 0; - if (idx > 5) idx = 5; + if (idx > 6) idx = 6; if (g_style_mats[idx]) return g_style_mats[idx]; if (g_style_mats[3]) @@ -223,8 +223,7 @@ void *chams_material() { return viewmodel_material(); } -// Viewmodels need a model shader; the VGUI white materials used by some -// styles do not draw skinned models and make the hands/weapon disappear. +// Viewmodels need a model shader; vgui materials do not draw skinned models. void *viewmodel_material() { if (g_style_mats[3]) return g_style_mats[3]; @@ -249,13 +248,15 @@ void chams_color(void *mat, float r, float g, float b) { vfunc(mat, 28)(mat, r, g, b); } -void chams_apply(void *mat, bool ignorez, int style, bool force_white) { +void chams_apply(void *mat, bool ignorez, int style, bool force_white, + const int *rgb) { using FlagFn = void (*)(void *, int, bool); - float r = force_white ? 1.0f : (float)g_chams_rgb[0] / 255.0f; - float g = force_white ? 1.0f : (float)g_chams_rgb[1] / 255.0f; - float b = force_white ? 1.0f : (float)g_chams_rgb[2] / 255.0f; + float r = force_white ? 1.0f : (float)rgb[0] / 255.0f; + 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); - // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright. + // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright, + // 6 Glow outline (solid base; the wireframe overlay is a second draw). vfunc(mat, 29)(mat, 1 << 15 /* IGNOREZ */, ignorez); vfunc(mat, 29)(mat, 1 << 28 /* WIREFRAME */, style == 1); vfunc(mat, 29)(mat, 1 << 7 /* ADDITIVE */, style == 2); @@ -282,31 +283,50 @@ void hkdModelExecute(void *self, const void *state, const void *info, // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit. int idx = *(const int *)((const char *)info + 0x30); 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) { - int style = g_chams_mat_index; - if (style < 0) style = 0; - if (style > 5) style = 5; - // VGUI materials do not draw skinned models: use the solid model - // shader for the viewmodel with the same flags (flat/additive). - bool vgui_style = (style == 0 || style == 2); - void *mat = (is_vm && vgui_style) ? viewmodel_material() - : chams_material(); - if (!mat) - mat = viewmodel_material(); - if (mat) { + ChamsGroup *grp = nullptr; + if (!is_vm) { + int rel = esp_npc_relation(idx); + if (rel >= 0) + grp = rel == 0 ? &g_chams_hostile + : rel == 1 ? &g_chams_neutral : &g_chams_friendly; + } + if (is_vm || grp) { + auto draw_pass = [&](int style, const int *rgb, + bool ignorez) -> bool { + if (style < 0) style = 0; + if (style > 6) style = 6; + void *mat = style_material(style); + if (!mat) + return false; bool white = style == 4; - if (g_chams_walls && !is_vm) { - chams_apply(mat, true, style, white); - g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); - g_orig_model_exec(self, state, info, bone); - } - chams_apply(mat, false, style, white); - g_force_override(self, mat, 0); + chams_apply(mat, ignorez, style, white, rgb); + g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); g_orig_model_exec(self, state, info, bone); - g_force_override(self, nullptr, 0); + if (style == 6) { + // Solid base plus a wireframe contour pass for depth. + void *wire = g_style_mats[1]; + if (wire && wire != mat) { + chams_apply(wire, ignorez, 1, false, rgb); + g_force_override(self, wire, 0); + g_orig_model_exec(self, state, info, bone); + chams_reset(wire); + } + } chams_reset(mat); + return true; + }; + bool did = false; + if (is_vm) { + did = draw_pass(g_chams_vm_mat, g_chams_vm_rgb, false); + } else { + if (grp->inv_on) + did |= draw_pass(grp->inv_mat, grp->inv_rgb, true); + if (grp->vis_on) + did |= draw_pass(grp->vis_mat, grp->vis_rgb, false); + } + if (did) { + g_force_override(self, nullptr, 0); return; } } @@ -337,7 +357,7 @@ void chams_preload() { void *probe = world_find_material("vgui/white"); logf("chams: probe vgui/white = %p", probe); } - for (int i = 0; i < 6; i++) { + for (int i = 0; i < 7; i++) { if (g_style_mats[i]) continue; void *m = world_find_material(chams_material_name(i)); -- cgit v1.2.3