From c35fc21f345ac9ecf9017115c4481a1147d05206 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 21:35:44 +0100 Subject: Chams: real additive material, addref cached materials Testing on :99 showed ADDITIVE on debugtranslucentsinglecolor did nothing and alpha lost the colour, because that VMT has no $translucent and the engine bound an unreferenced material. Use debug/debugtranslucentmodelhulls (VMT has $additive 1 + $color) for Additive/Fullbright, and addref every preloaded material so the engine stops warning "bad reference count 0" and applies $color/ColorModulate. Verified: Solid is flat blue, Additive is a translucent coloured glow, Fullbright is a white additive glow, White is opaque white. Also drop the viewmodel additive base (the solid material is opaque, so it made an opaque blob) and never call FindMaterial inside DrawModelExecute. Document that the alpha control is stored but has no effect until a custom $translucent model VMT is added. --- src/studio.cpp | 46 +++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/studio.cpp b/src/studio.cpp index 086d213..cf145d1 100644 --- a/src/studio.cpp +++ b/src/studio.cpp @@ -199,20 +199,21 @@ ModelExecFn g_orig_model_exec = nullptr; ForceOverrideFn g_force_override = nullptr; void *g_style_mats[7] = {}; -void *viewmodel_material(); - const char *chams_material_name(int idx) { switch (idx) { case 1: return "debug/debugwireframe"; case 2: + case 5: return "debug/debugtranslucentmodelhulls"; case 3: case 4: - case 5: case 6: default: return "debug/debugtranslucentsinglecolor"; } } +// Never call FindMaterial here: this runs inside DrawModelExecute, where +// loading a material can crash the material system. chams_preload fills the +// cache from outer_draw instead. void *style_material(int idx) { if (idx < 0) idx = 0; if (idx > 6) idx = 6; @@ -220,17 +221,7 @@ void *style_material(int idx) { return g_style_mats[idx]; if (g_style_mats[3]) return g_style_mats[3]; - return viewmodel_material(); -} - -// 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]; - static void *mat = nullptr; - if (!mat) - mat = world_find_material("debug/debugtranslucentsinglecolor"); - return mat; + return nullptr; } void chams_color(void *mat, float r, float g, float b) { @@ -260,6 +251,7 @@ void chams_alpha(void *mat, float a) { void chams_apply(void *mat, bool ignorez, int style, bool force_white, const int *rgb, bool znearer) { using FlagFn = void (*)(void *, int, bool); + bool additive = style == 2 || style == 5; 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; @@ -268,12 +260,12 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white, // 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). + // Styles 2/5 use debug/debugtranslucentmodelhulls, whose VMT already has + // $additive; ADDITIVE on debugtranslucentsinglecolor did nothing. 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 */, - style == 2 || style == 5); + vfunc(mat, 29)(mat, 1 << 7 /* ADDITIVE */, additive); vfunc(mat, 29)(mat, 1 << 12 /* FLAT */, style == 0); vfunc(mat, 29)(mat, 1 << 6 /* SELFILLUM */, style == 5); } @@ -281,7 +273,6 @@ 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); @@ -317,21 +308,6 @@ void hkdModelExecute(void *self, const void *state, const void *info, return false; bool white = style == 4 || style == 5; const int *use_rgb = rgb; - // Additive blending vanishes against bright backgrounds (sky, - // white walls, strong fog), which made the viewmodel disappear in - // 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); - chams_reset(mat); - } chams_apply(mat, ignorez, style, white, use_rgb, false); g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); g_orig_model_exec(self, state, info, bone); @@ -391,6 +367,10 @@ void chams_preload() { continue; void *m = world_find_material(chams_material_name(i)); if (m) { + // FindMaterial can hand back an unreferenced material; the engine warns + // ("bad reference count 0") and may reset its vars when it is bound. + using RefFn = void (*)(void *); + vfunc(m, 12)(m); // IMaterial::IncrementReferenceCount g_style_mats[i] = m; logf("chams: preloaded style %d %s = %p", i, chams_material_name(i), m); } -- cgit v1.2.3