summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 18:15:18 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 18:15:18 +0100
commit83b36aaaf146325af2d93dfbaf21eccba91a5d74 (patch)
tree2f6cd5a1d38231df4a6650e223681863f651e1df
parentChams: material styles and viewmodel support (diff)
downloadhl2h-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.md15
-rw-r--r--src/gui.cpp136
-rw-r--r--src/world.cpp73
3 files changed, 180 insertions, 44 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index c95095b..9233517 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -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
189enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET, ACT_SKY_RELOAD }; 190enum 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}
290static 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
286static int ctrl_val(const Ctrl &c) { 299static 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
914static int label_max_w(const Ctrl &c) { 936static 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.
1107static int g_dropdown_ctrl = -1;
1108
1109static 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
1127static 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}
1084static bool picker_rects_for(int ctrl_index, PickerRects &out) { 1152static 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
1895static void draw_tooltip() { 2011static 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}