summaryrefslogtreecommitdiffstats
path: root/src/bones.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bones.cpp')
-rw-r--r--src/bones.cpp36
1 files changed, 25 insertions, 11 deletions
diff --git a/src/bones.cpp b/src/bones.cpp
index c777067..c4168a9 100644
--- a/src/bones.cpp
+++ b/src/bones.cpp
@@ -100,7 +100,10 @@ struct BoneEntry {
100 Matrix3x4 mats[MAX_BONES]; 100 Matrix3x4 mats[MAX_BONES];
101}; 101};
102 102
103static BoneEntry g_entries[64]; 103// Sized for the 128-target scan with headroom: failed updates also allocate an
104// entry, and 64 slots evicted live NPCs mid-frame.
105constexpr int BONE_CACHE = 192;
106static BoneEntry g_entries[BONE_CACHE];
104static int g_next = 0; 107static int g_next = 0;
105static int g_logged = 0; 108static int g_logged = 0;
106static bool g_renderable_warned = false; 109static bool g_renderable_warned = false;
@@ -112,14 +115,14 @@ static double bone_now() {
112} 115}
113 116
114static BoneEntry *find_entry(int idx) { 117static BoneEntry *find_entry(int idx) {
115 for (int i = 0; i < 64; i++) 118 for (int i = 0; i < BONE_CACHE; i++)
116 if (g_entries[i].ent && g_entries[i].idx == idx) 119 if (g_entries[i].ent && g_entries[i].idx == idx)
117 return &g_entries[i]; 120 return &g_entries[i];
118 return nullptr; 121 return nullptr;
119} 122}
120 123
121static BoneEntry *alloc_entry(int idx, void *ent) { 124static BoneEntry *alloc_entry(int idx, void *ent) {
122 BoneEntry *e = &g_entries[g_next++ % 64]; 125 BoneEntry *e = &g_entries[g_next++ % BONE_CACHE];
123 memset(e, 0, sizeof(*e)); 126 memset(e, 0, sizeof(*e));
124 e->idx = idx; 127 e->idx = idx;
125 e->ent = ent; 128 e->ent = ent;
@@ -321,13 +324,17 @@ bool bones_update(int idx, void *ent) {
321 324
322 const void *expected = 325 const void *expected =
323 vfunc<GetModelIdxFn>(g_iface.modelinfo, 2)(g_iface.modelinfo, modelindex); 326 vfunc<GetModelIdxFn>(g_iface.modelinfo, 2)(g_iface.modelinfo, modelindex);
324 if (!expected) 327 if (!expected) {
328 e->valid = false;
325 return false; 329 return false;
330 }
326 331
327 // IClientUnknown::GetClientRenderable = primary vtable slot 6. 332 // IClientUnknown::GetClientRenderable = primary vtable slot 6.
328 void *r = vfunc<GetRenderableFn>(ent, 6)(ent); 333 void *r = vfunc<GetRenderableFn>(ent, 6)(ent);
329 if (!r) 334 if (!r) {
335 e->valid = false;
330 return false; 336 return false;
337 }
331 // Validate the interface before using SetupBones on it. 338 // Validate the interface before using SetupBones on it.
332 const void *rmodel = vfunc<GetModelFn>(r, 9)(r); 339 const void *rmodel = vfunc<GetModelFn>(r, 9)(r);
333 if (rmodel != expected) { 340 if (rmodel != expected) {
@@ -418,9 +425,14 @@ int bone_nearest_core_parent(int idx, int bone) {
418 return -1; 425 return -1;
419 int p = bone_at(e->hdr, bone)->parent; 426 int p = bone_at(e->hdr, bone)->parent;
420 if (!e->humanoid) { 427 if (!e->humanoid) {
421 while (p >= 0 && p < e->count && !e->bone_valid[p]) 428 // Same cycle guard as the core path: a malformed model can self-parent a
429 // bone whose matrix is invalid, which would spin here forever.
430 for (int guard = 0; p >= 0 && p < e->count && guard < 64; guard++) {
431 if (e->bone_valid[p])
432 return p;
422 p = bone_at(e->hdr, p)->parent; 433 p = bone_at(e->hdr, p)->parent;
423 return (p >= 0 && p < e->count) ? p : -1; 434 }
435 return -1;
424 } 436 }
425 for (int guard = 0; p >= 0 && p < e->count && guard < 64; guard++) { 437 for (int guard = 0; p >= 0 && p < e->count && guard < 64; guard++) {
426 if (e->core[p] && e->bone_valid[p]) 438 if (e->core[p] && e->bone_valid[p])
@@ -487,14 +499,16 @@ bool head_position(int idx, Vector &out) {
487 if (boxes[i].bone < 0 || boxes[i].bone >= e->count) 499 if (boxes[i].bone < 0 || boxes[i].bone >= e->count)
488 continue; 500 continue;
489 const char *bn = bone_name_raw(e->hdr, boxes[i].bone); 501 const char *bn = bone_name_raw(e->hdr, boxes[i].bone);
490 if (bn && head_like_name(bn)) 502 // Keep looking: the first name match can be a bone whose matrix failed
491 return hitbox_world(e, boxes[i], out); 503 // validation, and returning its false result aborted the whole search.
504 if (bn && head_like_name(bn) && hitbox_world(e, boxes[i], out))
505 return true;
492 } 506 }
493 } 507 }
494 for (int i = 0; i < e->count; i++) { 508 for (int i = 0; i < e->count; i++) {
495 const char *bn = bone_name_raw(e->hdr, i); 509 const char *bn = bone_name_raw(e->hdr, i);
496 if (bn && head_like_name(bn)) 510 if (bn && head_like_name(bn) && bone_pos(idx, i, out))
497 return bone_pos(idx, i, out); 511 return true;
498 } 512 }
499 // Last resort for non-humanoid models with unnamed heads/hitboxes: the 513 // Last resort for non-humanoid models with unnamed heads/hitboxes: the
500 // first hitbox is the body (still a valid aim point). 514 // first hitbox is the body (still a valid aim point).