summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-26 20:33:46 +0100
committersalmonik <salmonik@tuta.io>2026-10-26 20:33:46 +0100
commit0d0e0602ed89ef6950c27657889a9dcceb0cc697 (patch)
treec9817ad3752a269122898365fc3e8adc16d1f692
parentFonts: recreate the VGUI font after menu/map transitions (diff)
downloadhl2h-0d0e0602ed89ef6950c27657889a9dcceb0cc697.tar.gz
hl2h-0d0e0602ed89ef6950c27657889a9dcceb0cc697.zip
Bones: ignore uninitialized/zero bone matrices
- validate each SetupBones matrix (finite and non-zero world translation); extra/modded and some vanilla bones (Father Grigori) can be uninitialized and were drawing lines to world (0,0,0) - invalid bones are skipped by the skeleton, hitbox centers and aim points; skeleton children connect to the nearest valid ancestor instead of a dead bone - log the per-model invalid bone count with the first bones diagnostics
Diffstat (limited to '')
-rw-r--r--src/bones.cpp36
1 files changed, 31 insertions, 5 deletions
diff --git a/src/bones.cpp b/src/bones.cpp
index 1940449..787af1b 100644
--- a/src/bones.cpp
+++ b/src/bones.cpp
@@ -6,6 +6,7 @@
6// Everything here is client-side; no server access is involved. 6// Everything here is client-side; no server access is involved.
7 7
8#include "hl2h.h" 8#include "hl2h.h"
9#include <cmath>
9#include <cstdint> 10#include <cstdint>
10#include <cstring> 11#include <cstring>
11#include <ctime> 12#include <ctime>
@@ -95,6 +96,7 @@ struct BoneEntry {
95 const StudioHdr *hdr; 96 const StudioHdr *hdr;
96 int count; 97 int count;
97 bool core[MAX_BONES]; 98 bool core[MAX_BONES];
99 bool bone_valid[MAX_BONES];
98 Matrix3x4 mats[MAX_BONES]; 100 Matrix3x4 mats[MAX_BONES];
99}; 101};
100 102
@@ -138,6 +140,17 @@ static const char *bone_name_raw(const StudioHdr *h, int i) {
138 return n[0] ? n : nullptr; 140 return n[0] ? n : nullptr;
139} 141}
140 142
143// SetupBones can leave extra/modded bones uninitialized; they show up as a
144// zero matrix and would draw a line to world (0,0,0).
145static bool matrix_ok(const Matrix3x4 &m) {
146 for (int r = 0; r < 3; r++)
147 for (int c = 0; c < 4; c++)
148 if (!std::isfinite(m.m[r][c]))
149 return false;
150 float x = m.m[0][3], y = m.m[1][3], z = m.m[2][3];
151 return (fabsf(x) + fabsf(y) + fabsf(z)) > 0.01f;
152}
153
141static const HitboxSet *hitbox_set0(const StudioHdr *h) { 154static const HitboxSet *hitbox_set0(const StudioHdr *h) {
142 if (!h || h->numhitboxsets <= 0 || h->hitboxsetindex <= 0) 155 if (!h || h->numhitboxsets <= 0 || h->hitboxsetindex <= 0)
143 return nullptr; 156 return nullptr;
@@ -181,7 +194,7 @@ static bool head_like_name(const char *n) {
181} 194}
182 195
183static bool hitbox_world(const BoneEntry *e, const Hitbox &b, Vector &out) { 196static bool hitbox_world(const BoneEntry *e, const Hitbox &b, Vector &out) {
184 if (b.bone < 0 || b.bone >= e->count) 197 if (b.bone < 0 || b.bone >= e->count || !e->bone_valid[b.bone])
185 return false; 198 return false;
186 Vector local{(b.bbmin.x + b.bbmax.x) * 0.5f, 199 Vector local{(b.bbmin.x + b.bbmax.x) * 0.5f,
187 (b.bbmin.y + b.bbmax.y) * 0.5f, 200 (b.bbmin.y + b.bbmax.y) * 0.5f,
@@ -347,11 +360,18 @@ bool bones_update(int idx, void *ent) {
347 e->hdr = hdr; 360 e->hdr = hdr;
348 e->count = hdr->numbones; 361 e->count = hdr->numbones;
349 build_core_mask(e); 362 build_core_mask(e);
363 int invalid_bones = 0;
364 for (int i = 0; i < e->count; i++) {
365 e->bone_valid[i] = matrix_ok(e->mats[i]);
366 if (!e->bone_valid[i])
367 invalid_bones++;
368 }
350 e->valid = true; 369 e->valid = true;
351 e->stamp = now; 370 e->stamp = now;
352 if (g_logged < 4) { 371 if (g_logged < 4) {
353 g_logged++; 372 g_logged++;
354 logf("bones: idx=%d count=%d bone0=%s", idx, e->count, 373 logf("bones: idx=%d count=%d invalid=%d bone0=%s", idx, e->count,
374 invalid_bones,
355 bone_name_raw(hdr, 0) ? bone_name_raw(hdr, 0) : "?"); 375 bone_name_raw(hdr, 0) ? bone_name_raw(hdr, 0) : "?");
356 } 376 }
357 return true; 377 return true;
@@ -385,6 +405,8 @@ bool bone_is_skeleton_core(int idx, int bone) {
385 BoneEntry *e = find_entry(idx); 405 BoneEntry *e = find_entry(idx);
386 if (!e || !e->valid || bone < 0 || bone >= e->count) 406 if (!e || !e->valid || bone < 0 || bone >= e->count)
387 return false; 407 return false;
408 if (!e->bone_valid[bone])
409 return false;
388 if (!e->humanoid) 410 if (!e->humanoid)
389 return true; // non-humanoid: draw its full bone tree 411 return true; // non-humanoid: draw its full bone tree
390 return e->core[bone]; 412 return e->core[bone];
@@ -395,10 +417,13 @@ int bone_nearest_core_parent(int idx, int bone) {
395 if (!e || !e->valid || bone < 0 || bone >= e->count) 417 if (!e || !e->valid || bone < 0 || bone >= e->count)
396 return -1; 418 return -1;
397 int p = bone_at(e->hdr, bone)->parent; 419 int p = bone_at(e->hdr, bone)->parent;
398 if (!e->humanoid) 420 if (!e->humanoid) {
421 while (p >= 0 && p < e->count && !e->bone_valid[p])
422 p = bone_at(e->hdr, p)->parent;
399 return (p >= 0 && p < e->count) ? p : -1; 423 return (p >= 0 && p < e->count) ? p : -1;
424 }
400 for (int guard = 0; p >= 0 && p < e->count && guard < 64; guard++) { 425 for (int guard = 0; p >= 0 && p < e->count && guard < 64; guard++) {
401 if (e->core[p]) 426 if (e->core[p] && e->bone_valid[p])
402 return p; 427 return p;
403 p = bone_at(e->hdr, p)->parent; 428 p = bone_at(e->hdr, p)->parent;
404 } 429 }
@@ -412,7 +437,8 @@ bool bones_is_humanoid(int idx) {
412 437
413bool bone_pos(int idx, int bone, Vector &out) { 438bool bone_pos(int idx, int bone, Vector &out) {
414 BoneEntry *e = find_entry(idx); 439 BoneEntry *e = find_entry(idx);
415 if (!e || !e->valid || bone < 0 || bone >= e->count) 440 if (!e || !e->valid || bone < 0 || bone >= e->count ||
441 !e->bone_valid[bone])
416 return false; 442 return false;
417 const Matrix3x4 &m = e->mats[bone]; 443 const Matrix3x4 &m = e->mats[bone];
418 out = {m.m[0][3], m.m[1][3], m.m[2][3]}; 444 out = {m.m[0][3], m.m[1][3], m.m[2][3]};