diff options
Diffstat (limited to '')
| -rw-r--r-- | src/studio.cpp | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/studio.cpp b/src/studio.cpp index 23be97f..1b7b77d 100644 --- a/src/studio.cpp +++ b/src/studio.cpp | |||
| @@ -181,8 +181,106 @@ std::string loaded_module_path(const char *needle) { | |||
| 181 | return found; | 181 | return found; |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | // --------------------------------------------------------------------------- | ||
| 185 | // chams: force a flat material on NPC draws | ||
| 186 | // --------------------------------------------------------------------------- | ||
| 187 | // | ||
| 188 | // IVModelRender::DrawModelExecute is called once per model. For NPC entities | ||
| 189 | // we force models/debug/debugwhite, optionally with MATERIAL_VAR_IGNOREZ for | ||
| 190 | // the through-wall pass, and draw twice. IVModelRender::ForcedMaterialOverride | ||
| 191 | // is slot 1; DrawModelExecute is slot 19 in VEngineModel016. | ||
| 192 | |||
| 193 | using ModelExecFn = void (*)(void *, const void *, const void *, void *); | ||
| 194 | using ForceOverrideFn = void (*)(void *, void *, int); | ||
| 195 | |||
| 196 | void *g_modelrender = nullptr; | ||
| 197 | ModelExecFn g_orig_model_exec = nullptr; | ||
| 198 | ForceOverrideFn g_force_override = nullptr; | ||
| 199 | void *g_chams_mat = nullptr; | ||
| 200 | bool g_chams_mat_tried = false; | ||
| 201 | |||
| 202 | void *chams_material() { | ||
| 203 | if (g_chams_mat_tried) | ||
| 204 | return g_chams_mat; | ||
| 205 | g_chams_mat_tried = true; | ||
| 206 | g_chams_mat = world_find_material("models/debug/debugwhite"); | ||
| 207 | if (g_chams_mat) | ||
| 208 | logf("chams: material models/debug/debugwhite = %p", g_chams_mat); | ||
| 209 | else | ||
| 210 | logf("chams: models/debug/debugwhite not found"); | ||
| 211 | return g_chams_mat; | ||
| 212 | } | ||
| 213 | |||
| 214 | void chams_apply(void *mat, bool ignorez) { | ||
| 215 | using ColorFn = void (*)(void *, float, float, float); | ||
| 216 | using FlagFn = void (*)(void *, int, bool); | ||
| 217 | vfunc<ColorFn>(mat, 28)(mat, (float)g_chams_rgb[0] / 255.0f, | ||
| 218 | (float)g_chams_rgb[1] / 255.0f, | ||
| 219 | (float)g_chams_rgb[2] / 255.0f); | ||
| 220 | vfunc<FlagFn>(mat, 29)(mat, 1 << 15 /* MATERIAL_VAR_IGNOREZ */, ignorez); | ||
| 221 | } | ||
| 222 | |||
| 223 | void hkdModelExecute(void *self, const void *state, const void *info, | ||
| 224 | void *bone) { | ||
| 225 | if (!g_orig_model_exec) | ||
| 226 | return; | ||
| 227 | if (feature_active(FEAT_CHAMS) && info && g_force_override) { | ||
| 228 | // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit. | ||
| 229 | int idx = *(const int *)((const char *)info + 0x30); | ||
| 230 | if (idx > 0 && idx <= 8192 && esp_is_npc_index(idx)) { | ||
| 231 | void *mat = chams_material(); | ||
| 232 | if (mat) { | ||
| 233 | if (g_chams_walls) { | ||
| 234 | chams_apply(mat, true); | ||
| 235 | g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); | ||
| 236 | g_orig_model_exec(self, state, info, bone); | ||
| 237 | } | ||
| 238 | chams_apply(mat, false); | ||
| 239 | g_force_override(self, mat, 0); | ||
| 240 | g_orig_model_exec(self, state, info, bone); | ||
| 241 | g_force_override(self, nullptr, 0); | ||
| 242 | // debugwhite is shared: restore colour and depth behaviour. | ||
| 243 | using ColorFn = void (*)(void *, float, float, float); | ||
| 244 | using FlagFn = void (*)(void *, int, bool); | ||
| 245 | vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false); | ||
| 246 | vfunc<ColorFn>(mat, 28)(mat, 1.0f, 1.0f, 1.0f); | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | g_orig_model_exec(self, state, info, bone); | ||
| 252 | } | ||
| 253 | |||
| 184 | } // namespace | 254 | } // namespace |
| 185 | 255 | ||
| 256 | void chams_init() { | ||
| 257 | if (g_modelrender || !g_iface.modelrender) { | ||
| 258 | if (!g_iface.modelrender) | ||
| 259 | logf("chams: VEngineModel016 unavailable"); | ||
| 260 | return; | ||
| 261 | } | ||
| 262 | void **vt = *(void ***)g_iface.modelrender; | ||
| 263 | if (!vt) { | ||
| 264 | logf("chams: model render has no vtable"); | ||
| 265 | return; | ||
| 266 | } | ||
| 267 | g_force_override = (ForceOverrideFn)vt[1]; | ||
| 268 | g_orig_model_exec = (ModelExecFn)vt[19]; | ||
| 269 | if (!g_engine.contains((uintptr_t)g_force_override) || | ||
| 270 | !g_engine.contains((uintptr_t)g_orig_model_exec)) { | ||
| 271 | logf("chams: implausible vtable entries override=%p draw=%p", | ||
| 272 | (void *)g_force_override, (void *)g_orig_model_exec); | ||
| 273 | return; | ||
| 274 | } | ||
| 275 | static void *nvt[32]; | ||
| 276 | memcpy(nvt, vt, sizeof(nvt)); | ||
| 277 | nvt[19] = (void *)&hkdModelExecute; | ||
| 278 | g_modelrender = g_iface.modelrender; | ||
| 279 | *(void ***)g_modelrender = nvt; | ||
| 280 | logf("chams: IVModelRender %p hooked (draw=%p override=%p)", g_modelrender, | ||
| 281 | (void *)g_orig_model_exec, (void *)g_force_override); | ||
| 282 | } | ||
| 283 | |||
| 186 | void studio_props_init() { | 284 | void studio_props_init() { |
| 187 | if (g_install_tried) | 285 | if (g_install_tried) |
| 188 | return; | 286 | return; |
