diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 18:15:18 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 18:15:18 +0100 |
| commit | 83b36aaaf146325af2d93dfbaf21eccba91a5d74 (patch) | |
| tree | 2f6cd5a1d38231df4a6650e223681863f651e1df | |
| parent | Chams: material styles and viewmodel support (diff) | |
| download | hl2h-83b36aaaf146325af2d93dfbaf21eccba91a5d74.tar.gz hl2h-83b36aaaf146325af2d93dfbaf21eccba91a5d74.zip | |
Chams: exact material styles, GUI dropdown, viewmodel hookup
- world_find_material now tries the exact requested style first (resident,
then FindMaterial) before falling back to debugwhite/vgui white. The old
candidate scan returned vgui/white for every style, so all materials looked
flat.
- Add a real CK_DROPDOWN control (overlay list, outside-click close, config
save) and use it for the Chams material selector.
- Chams viewmodel toggle included in config/UI.
| -rw-r--r-- | CONTEXT.md | 15 | ||||
| -rw-r--r-- | src/gui.cpp | 136 | ||||
| -rw-r--r-- | src/world.cpp | 73 |
3 files changed, 180 insertions, 44 deletions
| @@ -111,6 +111,10 @@ vocabulary / decisions. | |||
| 111 | - **Category**: sidebar group. Movement, Combat, Visuals, Weapons, System. | 111 | - **Category**: sidebar group. Movement, Combat, Visuals, Weapons, System. |
| 112 | - **Card**: per-feature panel. Header always shows Active + Bind; body is a | 112 | - **Card**: per-feature panel. Header always shows Active + Bind; body is a |
| 113 | collapsible set of settings. | 113 | collapsible set of settings. |
| 114 | - **Dropdown**: `CK_DROPDOWN` row with a value button; clicking opens an | ||
| 115 | overlay list below the row (above it near the window bottom). Clicking an | ||
| 116 | option sets the value and saves the config; clicking elsewhere closes it. | ||
| 117 | One dropdown is open at a time; it is not clipped by the card/content rect. | ||
| 114 | - **Text layout**: measured with `ISurface::GetTextSize` and wrapped to the | 118 | - **Text layout**: measured with `ISurface::GetTextSize` and wrapped to the |
| 115 | available width. Card headers, control rows, info rows, tooltips and toasts | 119 | available width. Card headers, control rows, info rows, tooltips and toasts |
| 116 | grow/wrap instead of truncating; there is no ellipsis truncation. The config | 120 | grow/wrap instead of truncating; there is no ellipsis truncation. The config |
| @@ -293,16 +297,15 @@ vocabulary / decisions. | |||
| 293 | materials ignore ColorModulate. The shared material's flag and colour are | 297 | materials ignore ColorModulate. The shared material's flag and colour are |
| 294 | restored to (false, white) after the draw. | 298 | restored to (false, white) after the draw. |
| 295 | - Material lookup walks the `IMaterialSystem` list (`world_find_material`), with | 299 | - Material lookup walks the `IMaterialSystem` list (`world_find_material`), with |
| 296 | `FindMaterial` (slot 71) as the fallback. The style index selects: | 300 | `FindMaterial` (slot 71) as the fallback. The **exact requested style** is |
| 297 | 0 `models/debug/debugwhite`, 1 `debug/debugwireframevertexcolorignorez`, | 301 | tried first (resident, then loaded on demand), and only if that fails does it |
| 298 | 2 `vgui/white_additive`, 3 `debug/debugtranslucentsinglecolor`, | 302 | fall back to `models/debug/debugwhite`/`vgui/white`. Styles: Flat, Wireframe, |
| 299 | 4 `vgui/white`; if the requested style is unavailable it falls back to the | 303 | Additive glow, Solid, White. The chosen material name is logged. |
| 300 | first available debugwhite candidate. The chosen material name is logged. | ||
| 301 | - `Viewmodel (hands/weapon)` also applies the override to entities whose class | 304 | - `Viewmodel (hands/weapon)` also applies the override to entities whose class |
| 302 | contains `viewmodel` or whose model path is `.../v_...`. The viewmodel gets | 305 | contains `viewmodel` or whose model path is `.../v_...`. The viewmodel gets |
| 303 | only the normal-depth pass (no ignorez). | 306 | only the normal-depth pass (no ignorez). |
| 304 | - Feature is off by default; `[chams] active/bind/walls/viewmodel/material/r/g/b`, | 307 | - 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 | 308 | with Through walls, Viewmodel, a Material dropdown and colour in the Visuals |
| 306 | card. | 309 | card. |
| 307 | - This is a flat material override, not stencil outlines; depth handling is | 310 | - This is a flat material override, not stencil outlines; depth handling is |
| 308 | the simple ignorez/normal two-pass. | 311 | the simple ignorez/normal two-pass. |
diff --git a/src/gui.cpp b/src/gui.cpp index 4a949f0..4adc440 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -183,7 +183,8 @@ enum CtrlKind { | |||
| 183 | CK_SECTION, | 183 | CK_SECTION, |
| 184 | CK_INFO, | 184 | CK_INFO, |
| 185 | CK_BUTTON, | 185 | CK_BUTTON, |
| 186 | CK_COLOR | 186 | CK_COLOR, |
| 187 | CK_DROPDOWN | ||
| 187 | }; | 188 | }; |
| 188 | 189 | ||
| 189 | enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET, ACT_SKY_RELOAD }; | 190 | enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET, ACT_SKY_RELOAD }; |
| @@ -195,6 +196,8 @@ struct Ctrl { | |||
| 195 | bool *bval; // CK_TOGGLE | 196 | bool *bval; // CK_TOGGLE |
| 196 | int *ival; // CK_SLIDER | 197 | int *ival; // CK_SLIDER |
| 197 | int *rgb; // CK_COLOR: three ints, 0..255 | 198 | int *rgb; // CK_COLOR: three ints, 0..255 |
| 199 | const char *const *opts; // CK_DROPDOWN | ||
| 200 | int nopts; // CK_DROPDOWN | ||
| 198 | int min, max; | 201 | int min, max; |
| 199 | int action; // CK_BUTTON | 202 | int action; // CK_BUTTON |
| 200 | bool zero_unlimited; // slider: value 0 displays as "All" | 203 | bool zero_unlimited; // slider: value 0 displays as "All" |
| @@ -253,6 +256,8 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc, | |||
| 253 | c.action = action; | 256 | c.action = action; |
| 254 | c.zero_unlimited = false; | 257 | c.zero_unlimited = false; |
| 255 | c.rgb = nullptr; | 258 | c.rgb = nullptr; |
| 259 | c.opts = nullptr; | ||
| 260 | c.nopts = 0; | ||
| 256 | c.hue = 0.0f; | 261 | c.hue = 0.0f; |
| 257 | c.sat = 0.0f; | 262 | c.sat = 0.0f; |
| 258 | c.val = 1.0f; | 263 | c.val = 1.0f; |
| @@ -282,10 +287,20 @@ static void add_color(int card, const char *label, const char *desc, | |||
| 282 | add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); | 287 | add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); |
| 283 | g_ctrls[i].rgb = rgb; | 288 | g_ctrls[i].rgb = rgb; |
| 284 | } | 289 | } |
| 290 | static void add_dropdown(int card, const char *label, const char *desc, | ||
| 291 | const char *const *opts, int nopts, int *value) { | ||
| 292 | int i = g_nctrl; | ||
| 293 | add_ctrl(card, CK_DROPDOWN, label, desc, 0, nopts > 0 ? nopts - 1 : 0, | ||
| 294 | nullptr, value, ACT_NONE); | ||
| 295 | g_ctrls[i].opts = opts; | ||
| 296 | g_ctrls[i].nopts = nopts; | ||
| 297 | } | ||
| 285 | 298 | ||
| 286 | static int ctrl_val(const Ctrl &c) { | 299 | static int ctrl_val(const Ctrl &c) { |
| 287 | if (c.kind == CK_TOGGLE && c.bval) | 300 | if (c.kind == CK_TOGGLE && c.bval) |
| 288 | return *c.bval ? 1 : 0; | 301 | return *c.bval ? 1 : 0; |
| 302 | if (c.kind == CK_DROPDOWN) | ||
| 303 | return c.ival ? *c.ival : 0; | ||
| 289 | if (c.kind == CK_SLIDER && c.ival) | 304 | if (c.kind == CK_SLIDER && c.ival) |
| 290 | return *c.ival; | 305 | return *c.ival; |
| 291 | return 0; | 306 | return 0; |
| @@ -294,6 +309,13 @@ static void ctrl_set(Ctrl &c, int v) { | |||
| 294 | if (c.kind == CK_TOGGLE && c.bval) { | 309 | if (c.kind == CK_TOGGLE && c.bval) { |
| 295 | *c.bval = v != 0; | 310 | *c.bval = v != 0; |
| 296 | config_save(); | 311 | config_save(); |
| 312 | } else if (c.kind == CK_DROPDOWN && c.ival) { | ||
| 313 | if (v < c.min) v = c.min; | ||
| 314 | if (v > c.max) v = c.max; | ||
| 315 | if (*c.ival != v) { | ||
| 316 | *c.ival = v; | ||
| 317 | config_save(); | ||
| 318 | } | ||
| 297 | } else if (c.kind == CK_SLIDER && c.ival) { | 319 | } else if (c.kind == CK_SLIDER && c.ival) { |
| 298 | if (v < c.min) v = c.min; | 320 | if (v < c.min) v = c.min; |
| 299 | if (v > c.max) v = c.max; | 321 | if (v > c.max) v = c.max; |
| @@ -431,11 +453,11 @@ static void build_cards() { | |||
| 431 | add_toggle(chams, "Viewmodel (hands/weapon)", | 453 | add_toggle(chams, "Viewmodel (hands/weapon)", |
| 432 | "Apply chams to the first-person hands and weapon too.", | 454 | "Apply chams to the first-person hands and weapon too.", |
| 433 | &g_chams_viewmodel); | 455 | &g_chams_viewmodel); |
| 434 | add_slider(chams, "Material", | 456 | static const char *const chams_mat_names[] = { |
| 435 | "0 Flat, 1 Wireframe, 2 Additive glow, 3 Solid, 4 White.", 0, 4, | 457 | "Flat", "Wireframe", "Additive glow", "Solid", "White"}; |
| 436 | &g_chams_mat_index); | 458 | add_dropdown(chams, "Material", "Chams material.", chams_mat_names, 5, |
| 459 | &g_chams_mat_index); | ||
| 437 | add_color(chams, "Colour", "Chams colour.", g_chams_rgb); | 460 | add_color(chams, "Colour", "Chams colour.", g_chams_rgb); |
| 438 | |||
| 439 | int view = add_card(2, nullptr, "Camera / display", | 461 | int view = add_card(2, nullptr, "Camera / display", |
| 440 | "Camera FOV, viewmodel FOV and HDR brightness.", true); | 462 | "Camera FOV, viewmodel FOV and HDR brightness.", true); |
| 441 | add_slider(view, "Camera FOV", | 463 | add_slider(view, "Camera FOV", |
| @@ -912,7 +934,7 @@ static int header_h(const Card &c) { | |||
| 912 | } | 934 | } |
| 913 | 935 | ||
| 914 | static int label_max_w(const Ctrl &c) { | 936 | static int label_max_w(const Ctrl &c) { |
| 915 | if (c.kind == CK_SLIDER) | 937 | if (c.kind == CK_SLIDER || c.kind == CK_DROPDOWN) |
| 916 | return S(214.0f); | 938 | return S(214.0f); |
| 917 | return card_content_w() - S(16.0f) - S(110.0f); | 939 | return card_content_w() - S(16.0f) - S(110.0f); |
| 918 | } | 940 | } |
| @@ -1081,6 +1103,52 @@ static bool control_rect(int ctrl_index, Rect &out_card, Rect &out_row) { | |||
| 1081 | return false; | 1103 | return false; |
| 1082 | } | 1104 | } |
| 1083 | 1105 | ||
| 1106 | // Dropdown popup state (one open at a time). The value is the Ctrl index. | ||
| 1107 | static int g_dropdown_ctrl = -1; | ||
| 1108 | |||
| 1109 | static bool dropdown_rects(int ctrl_index, Rect &btn, Rect &pop, | ||
| 1110 | int &item_h) { | ||
| 1111 | Rect card, row; | ||
| 1112 | if (!control_rect(ctrl_index, card, row)) | ||
| 1113 | return false; | ||
| 1114 | Ctrl &ct = g_ctrls[ctrl_index]; | ||
| 1115 | if (ct.kind != CK_DROPDOWN || !ct.opts || ct.nopts <= 0) | ||
| 1116 | return false; | ||
| 1117 | int bw = S(150.0f); | ||
| 1118 | btn = Rect{card.x + card.w - S(16.0f) - bw, | ||
| 1119 | row.y + (row.h - S(26.0f)) / 2, bw, S(26.0f)}; | ||
| 1120 | item_h = S(22.0f); | ||
| 1121 | pop = Rect{btn.x, btn.y + btn.h, btn.w, item_h * ct.nopts}; | ||
| 1122 | if (pop.y + pop.h > g_win_y + g_win_h - S(34.0f)) | ||
| 1123 | pop.y = btn.y - pop.h; | ||
| 1124 | return true; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | static void draw_dropdown() { | ||
| 1128 | if (g_dropdown_ctrl < 0) | ||
| 1129 | return; | ||
| 1130 | Rect btn, pop; | ||
| 1131 | int ih = 0; | ||
| 1132 | if (!dropdown_rects(g_dropdown_ctrl, btn, pop, ih)) { | ||
| 1133 | g_dropdown_ctrl = -1; | ||
| 1134 | return; | ||
| 1135 | } | ||
| 1136 | Ctrl &ct = g_ctrls[g_dropdown_ctrl]; | ||
| 1137 | fill(pop, Col{25, 28, 34, 255}); | ||
| 1138 | outline(pop, C_BORDER); | ||
| 1139 | int cur = ct.ival ? *ct.ival : 0; | ||
| 1140 | for (int i = 0; i < ct.nopts; i++) { | ||
| 1141 | Rect it{pop.x, pop.y + i * ih, pop.w, ih}; | ||
| 1142 | if (it.has(g_mouse_x, g_mouse_y)) | ||
| 1143 | fill(it, Col{45, 50, 60, 255}); | ||
| 1144 | text_in(pop, it.x + S(9), it.y + (ih - g_body_h) / 2, ct.opts[i], | ||
| 1145 | g_font_body, g_body_h, it.w - S(28), | ||
| 1146 | i == cur ? C_ACCENT : C_TEXT); | ||
| 1147 | if (i == cur) | ||
| 1148 | fill(Rect{it.x + it.w - S(14), it.y + ih / 2 - S(3), S(6), S(6)}, | ||
| 1149 | C_ACCENT); | ||
| 1150 | } | ||
| 1151 | } | ||
| 1084 | static bool picker_rects_for(int ctrl_index, PickerRects &out) { | 1152 | static bool picker_rects_for(int ctrl_index, PickerRects &out) { |
| 1085 | if (ctrl_index < 0 || ctrl_index >= g_nctrl) | 1153 | if (ctrl_index < 0 || ctrl_index >= g_nctrl) |
| 1086 | return false; | 1154 | return false; |
| @@ -1219,6 +1287,7 @@ static void close_gui() { | |||
| 1219 | g_slider_drag = -1; | 1287 | g_slider_drag = -1; |
| 1220 | g_picker_ctrl = -1; | 1288 | g_picker_ctrl = -1; |
| 1221 | g_picker_drag = 0; | 1289 | g_picker_drag = 0; |
| 1290 | g_dropdown_ctrl = -1; | ||
| 1222 | g_reset_armed = false; | 1291 | g_reset_armed = false; |
| 1223 | input_clear_mouse_states(); | 1292 | input_clear_mouse_states(); |
| 1224 | if (g_sdl_ready) { | 1293 | if (g_sdl_ready) { |
| @@ -1341,6 +1410,29 @@ static void handle_mouse_click() { | |||
| 1341 | return; | 1410 | return; |
| 1342 | int mx = g_mouse_x, my = g_mouse_y; | 1411 | int mx = g_mouse_x, my = g_mouse_y; |
| 1343 | 1412 | ||
| 1413 | // Open dropdown: consume the click on its popup first. | ||
| 1414 | if (g_dropdown_ctrl >= 0) { | ||
| 1415 | Rect btn, pop; | ||
| 1416 | int ih = 0; | ||
| 1417 | if (dropdown_rects(g_dropdown_ctrl, btn, pop, ih)) { | ||
| 1418 | if (pop.has(mx, my)) { | ||
| 1419 | Ctrl &ct = g_ctrls[g_dropdown_ctrl]; | ||
| 1420 | int i = (my - pop.y) / ih; | ||
| 1421 | if (i >= 0 && i < ct.nopts && ct.ival) { | ||
| 1422 | *ct.ival = i; | ||
| 1423 | config_save(); | ||
| 1424 | } | ||
| 1425 | g_dropdown_ctrl = -1; | ||
| 1426 | return; | ||
| 1427 | } | ||
| 1428 | if (btn.has(mx, my)) { | ||
| 1429 | g_dropdown_ctrl = -1; | ||
| 1430 | return; | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | g_dropdown_ctrl = -1; | ||
| 1434 | } | ||
| 1435 | |||
| 1344 | // title bar: drag | 1436 | // title bar: drag |
| 1345 | Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; | 1437 | Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; |
| 1346 | if (title.has(mx, my)) { | 1438 | if (title.has(mx, my)) { |
| @@ -1457,6 +1549,14 @@ static void handle_mouse_click() { | |||
| 1457 | } | 1549 | } |
| 1458 | return; | 1550 | return; |
| 1459 | } | 1551 | } |
| 1552 | if (ct.kind == CK_DROPDOWN) { | ||
| 1553 | int idx = c.c0 + j; | ||
| 1554 | Rect btn, pop; | ||
| 1555 | int ih = 0; | ||
| 1556 | if (dropdown_rects(idx, btn, pop, ih) && btn.has(mx, my)) | ||
| 1557 | g_dropdown_ctrl = (g_dropdown_ctrl == idx) ? -1 : idx; | ||
| 1558 | return; | ||
| 1559 | } | ||
| 1460 | if (ct.kind == CK_COLOR) { | 1560 | if (ct.kind == CK_COLOR) { |
| 1461 | int idx = c.c0 + j; | 1561 | int idx = c.c0 + j; |
| 1462 | int base = label_row_h(ct); | 1562 | int base = label_row_h(ct); |
| @@ -1876,6 +1976,22 @@ static void draw_card(const Rect &clip, int index, int y) { | |||
| 1876 | on ? C_GOOD : C_DIM); | 1976 | on ? C_GOOD : C_DIM); |
| 1877 | } else if (ct.kind == CK_SLIDER) { | 1977 | } else if (ct.kind == CK_SLIDER) { |
| 1878 | draw_slider(clip, cr, by, rh, ct); | 1978 | draw_slider(clip, cr, by, rh, ct); |
| 1979 | } else if (ct.kind == CK_DROPDOWN) { | ||
| 1980 | int cur = ct.ival ? *ct.ival : 0; | ||
| 1981 | if (cur < 0) cur = 0; | ||
| 1982 | if (cur >= ct.nopts) cur = ct.nopts - 1; | ||
| 1983 | const char *opt = | ||
| 1984 | (ct.opts && cur >= 0 && cur < ct.nopts) ? ct.opts[cur] : "?"; | ||
| 1985 | int bw = S(150.0f); | ||
| 1986 | Rect btn{cr.x + cr.w - S(16.0f) - bw, | ||
| 1987 | by + (base_h - S(26.0f)) / 2, bw, S(26.0f)}; | ||
| 1988 | fill(btn, Col{45, 50, 60, 255}, &clip); | ||
| 1989 | outline(btn, g_dropdown_ctrl == c.c0 + j ? C_ACCENT : C_BORDER, | ||
| 1990 | &clip); | ||
| 1991 | text_in(clip, btn.x + S(9), btn.y + (btn.h - g_body_h) / 2, opt, | ||
| 1992 | g_font_body, g_body_h, btn.w - S(30), C_TEXT); | ||
| 1993 | text_in(clip, btn.x + btn.w - S(16), btn.y + (btn.h - g_body_h) / 2, | ||
| 1994 | "v", g_font_body, g_body_h, 0, C_DIM); | ||
| 1879 | } else if (ct.kind == CK_COLOR) { | 1995 | } else if (ct.kind == CK_COLOR) { |
| 1880 | int pr = ct.rgb ? ct.rgb[0] : 255; | 1996 | int pr = ct.rgb ? ct.rgb[0] : 255; |
| 1881 | int pg = ct.rgb ? ct.rgb[1] : 255; | 1997 | int pg = ct.rgb ? ct.rgb[1] : 255; |
| @@ -1893,6 +2009,13 @@ static void draw_card(const Rect &clip, int index, int y) { | |||
| 1893 | } | 2009 | } |
| 1894 | 2010 | ||
| 1895 | static void draw_tooltip() { | 2011 | static void draw_tooltip() { |
| 2012 | if (g_dropdown_ctrl >= 0) { | ||
| 2013 | Rect btn, pop; | ||
| 2014 | int ih = 0; | ||
| 2015 | if (dropdown_rects(g_dropdown_ctrl, btn, pop, ih) && | ||
| 2016 | (pop.has(g_mouse_x, g_mouse_y) || btn.has(g_mouse_x, g_mouse_y))) | ||
| 2017 | return; | ||
| 2018 | } | ||
| 1896 | if (!g_hover_desc || !g_hover_desc[0]) | 2019 | if (!g_hover_desc || !g_hover_desc[0]) |
| 1897 | return; | 2020 | return; |
| 1898 | float d = now_sec() - (float)g_hover_since; | 2021 | float d = now_sec() - (float)g_hover_since; |
| @@ -2102,6 +2225,7 @@ void gui_draw() { | |||
| 2102 | text_in(foot, hint_x, foot.y + S(6.0f), hint, g_font_body, g_body_h, 0, | 2225 | text_in(foot, hint_x, foot.y + S(6.0f), hint, g_font_body, g_body_h, 0, |
| 2103 | C_DIM); | 2226 | C_DIM); |
| 2104 | 2227 | ||
| 2228 | draw_dropdown(); | ||
| 2105 | draw_tooltip(); | 2229 | draw_tooltip(); |
| 2106 | } | 2230 | } |
| 2107 | 2231 | ||
diff --git a/src/world.cpp b/src/world.cpp index 897ad0a..a2e65cc 100644 --- a/src/world.cpp +++ b/src/world.cpp | |||
| @@ -1015,20 +1015,19 @@ void *world_find_material(const char *name) { | |||
| 1015 | using GetFn = void *(*)(void *, unsigned short); | 1015 | using GetFn = void *(*)(void *, unsigned short); |
| 1016 | using CountFn = int (*)(void *); | 1016 | using CountFn = int (*)(void *); |
| 1017 | using NameFn = const char *(*)(void *); | 1017 | using NameFn = const char *(*)(void *); |
| 1018 | using FindFn = void *(*)(void *, const char *, const char *, bool, | ||
| 1019 | const char *); | ||
| 1018 | FirstFn first = (FirstFn)vfunc<FirstFn>(ms, 73); | 1020 | FirstFn first = (FirstFn)vfunc<FirstFn>(ms, 73); |
| 1019 | NextFn next = (NextFn)vfunc<NextFn>(ms, 74); | 1021 | NextFn next = (NextFn)vfunc<NextFn>(ms, 74); |
| 1020 | InvalidFn invalid = (InvalidFn)vfunc<InvalidFn>(ms, 75); | 1022 | InvalidFn invalid = (InvalidFn)vfunc<InvalidFn>(ms, 75); |
| 1021 | GetFn get = (GetFn)vfunc<GetFn>(ms, 76); | 1023 | GetFn get = (GetFn)vfunc<GetFn>(ms, 76); |
| 1022 | CountFn count = (CountFn)vfunc<CountFn>(ms, 77); | 1024 | CountFn count = (CountFn)vfunc<CountFn>(ms, 77); |
| 1023 | const char *candidates[] = {name, | 1025 | static const char *groups[] = {"Model textures", "Other textures", nullptr}; |
| 1024 | "models/debug/debugwhite2", | 1026 | |
| 1025 | "models/debug/debugwhite3", | 1027 | auto walk = [&](const char *want) -> void * { |
| 1026 | "vgui/white", | 1028 | int total = count ? count(ms) : 0; |
| 1027 | "debug/debugtranslucentsinglecolor", | 1029 | if (total <= 0 || total >= 200000) |
| 1028 | "debug/debugwireframevertexcolorignorez", | 1030 | return nullptr; |
| 1029 | nullptr}; | ||
| 1030 | int total = count ? count(ms) : 0; | ||
| 1031 | if (total > 0 && total < 200000) { | ||
| 1032 | unsigned short h = first(ms); | 1031 | unsigned short h = first(ms); |
| 1033 | unsigned short inv = invalid(ms); | 1032 | unsigned short inv = invalid(ms); |
| 1034 | for (int i = 0; i < total && h != inv; i++) { | 1033 | for (int i = 0; i < total && h != inv; i++) { |
| @@ -1036,38 +1035,48 @@ void *world_find_material(const char *name) { | |||
| 1036 | if (mat) { | 1035 | if (mat) { |
| 1037 | NameFn mat_name = (NameFn)vfunc<NameFn>(mat, 0); | 1036 | NameFn mat_name = (NameFn)vfunc<NameFn>(mat, 0); |
| 1038 | const char *n = mat_name(mat); | 1037 | const char *n = mat_name(mat); |
| 1039 | if (n) { | 1038 | if (n && strcmp(n, want) == 0) |
| 1040 | for (int c = 0; candidates[c]; c++) | 1039 | return mat; |
| 1041 | if (strcmp(n, candidates[c]) == 0) { | ||
| 1042 | logf("chams: material %s = %p", n, mat); | ||
| 1043 | return mat; | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | } | 1040 | } |
| 1047 | h = next(ms, h); | 1041 | h = next(ms, h); |
| 1048 | } | 1042 | } |
| 1049 | } | 1043 | return nullptr; |
| 1050 | // Not resident yet: let the material system load it. | 1044 | }; |
| 1051 | using FindFn = void *(*)(void *, const char *, const char *, bool, | 1045 | auto find = [&](const char *want) -> void * { |
| 1052 | const char *); | ||
| 1053 | static const char *groups[] = {"Model textures", "Other textures", nullptr}; | ||
| 1054 | for (int c = 0; candidates[c]; c++) { | ||
| 1055 | for (int g = 0; groups[g]; g++) { | 1046 | for (int g = 0; groups[g]; g++) { |
| 1056 | void *mat = vfunc<FindFn>(ms, 71)(ms, candidates[c], groups[g], false, | 1047 | void *mat = vfunc<FindFn>(ms, 71)(ms, want, groups[g], false, nullptr); |
| 1057 | nullptr); | ||
| 1058 | if (!mat) | 1048 | if (!mat) |
| 1059 | continue; | 1049 | continue; |
| 1060 | NameFn mat_name = (NameFn)vfunc<NameFn>(mat, 0); | 1050 | NameFn mat_name = (NameFn)vfunc<NameFn>(mat, 0); |
| 1061 | const char *n = mat_name(mat); | 1051 | const char *n = mat_name(mat); |
| 1062 | if (!n) | 1052 | if (n && strcmp(n, want) == 0) |
| 1063 | continue; | 1053 | return mat; |
| 1064 | for (int k = 0; candidates[k]; k++) { | 1054 | } |
| 1065 | if (strcmp(n, candidates[k]) == 0) { | 1055 | return nullptr; |
| 1066 | logf("chams: material %s = %p (FindMaterial)", n, mat); | 1056 | }; |
| 1067 | return mat; | 1057 | |
| 1068 | } | 1058 | // First the exact style requested (resident or loaded on demand), then only |
| 1069 | } | 1059 | // the basic white fallbacks. Returning vgui/white before trying the exact |
| 1060 | // name made every style look identical. | ||
| 1061 | void *mat = walk(name); | ||
| 1062 | if (!mat) | ||
| 1063 | mat = find(name); | ||
| 1064 | if (mat) { | ||
| 1065 | logf("chams: material %s = %p", name, mat); | ||
| 1066 | return mat; | ||
| 1067 | } | ||
| 1068 | static const char *fallbacks[] = {"models/debug/debugwhite", "vgui/white", | ||
| 1069 | nullptr}; | ||
| 1070 | for (int i = 0; fallbacks[i]; i++) { | ||
| 1071 | mat = walk(fallbacks[i]); | ||
| 1072 | if (!mat) | ||
| 1073 | mat = find(fallbacks[i]); | ||
| 1074 | if (mat) { | ||
| 1075 | logf("chams: material %s (fallback for %s) = %p", fallbacks[i], name, | ||
| 1076 | mat); | ||
| 1077 | return mat; | ||
| 1070 | } | 1078 | } |
| 1071 | } | 1079 | } |
| 1080 | logf("chams: material %s not found", name); | ||
| 1072 | return nullptr; | 1081 | return nullptr; |
| 1073 | } | 1082 | } |
