1 struct skelbih
2 {
3     struct node
4     {
5         short split[2];
6         ushort child[2];
7 
axisskelbih::node8         int axis() const { return child[0]>>14; }
childindexskelbih::node9         int childindex(int which) const { return child[which]&0x3FFF; }
isleafskelbih::node10         bool isleaf(int which) const { return (child[1]&(1<<(14+which)))!=0; }
11     };
12 
13     struct tri : skelmodel::tri
14     {
15         uchar mesh, id;
16     };
17 
18     node *nodes;
19     int numnodes;
20     tri *tris;
21 
22     vec bbmin, bbmax;
23 
24     skelbih(skelmodel::skelmeshgroup *m, int numtris, tri *tris);
25 
~skelbihskelbih26     ~skelbih()
27     {
28         DELETEA(nodes);
29     }
30 
31     bool triintersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, int tidx, const vec &o, const vec &ray);
32 
33     void build(skelmodel::skelmeshgroup *m, ushort *indices, int numindices, const vec &vmin, const vec &vmax);
34 
35     void intersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const vec &o, const vec &ray, const vec &invray, node *curnode, float tmin, float tmax);
36     void intersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const vec &o, const vec &ray);
37 
calccenterskelbih38     vec calccenter() const
39     {
40         return vec(bbmin).add(bbmax).mul(0.5f);
41     }
42 
calcradiusskelbih43     float calcradius() const
44     {
45         return vec(bbmax).sub(bbmin).mul(0.5f).magnitude();
46     }
47 };
48 
49 #define SKELTRIINTERSECT(a, b, c) \
50     vec eb = vec(b).sub(a), ec = vec(c).sub(a); \
51     vec p; \
52     p.cross(ray, ec); \
53     float det = eb.dot(p); \
54     if(det == 0) return false; \
55     vec r = vec(o).sub(a); \
56     float u = r.dot(p) / det; \
57     if(u < 0 || u > 1) return false; \
58     vec q; \
59     q.cross(r, eb); \
60     float v = ray.dot(q) / det; \
61     if(v < 0 || u + v > 1) return false; \
62     float f = ec.dot(q) / det; \
63     if(f < 0 || f*skelmodel::intersectscale > skelmodel::intersectdist) return false; \
64     if(!(skelmodel::intersectmode&RAY_SHADOW) && tm->noclip) return false; \
65     if((skelmodel::intersectmode&RAY_ALPHAPOLY)==RAY_ALPHAPOLY) \
66     { \
67         Texture *tex = s[t.mesh].tex; \
68         if((tex->type&(Texture::ALPHA|Texture::COMPRESSED)) == Texture::ALPHA && (tex->alphamask || loadalphamask(tex))) \
69         { \
70             int si = clamp(int(tex->xs * (va.tc.x + u*(vb.tc.x - va.tc.x) + v*(vc.tc.x - va.tc.x))), 0, tex->xs-1), \
71                 ti = clamp(int(tex->ys * (va.tc.y + u*(vb.tc.y - va.tc.y) + v*(vc.tc.y - va.tc.y))), 0, tex->ys-1); \
72             if(!(tex->alphamask[ti*((tex->xs+7)/8) + si/8] & (1<<(si%8)))) return false; \
73         } \
74     } \
75     skelmodel::intersectdist = f*skelmodel::intersectscale; \
76     skelmodel::intersectresult = t.id&0x80 ? -1 : t.id; \
77     return true;
78 
triintersect(skelmodel::skelmeshgroup * m,skelmodel::skin * s,int tidx,const vec & o,const vec & ray)79 bool skelbih::triintersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, int tidx, const vec &o, const vec &ray)
80 {
81     const tri &t = tris[tidx];
82     skelmodel::skelmesh *tm = (skelmodel::skelmesh *)m->meshes[t.mesh];
83     const skelmodel::vert &va = tm->verts[t.vert[0]], &vb = tm->verts[t.vert[1]], &vc = tm->verts[t.vert[2]];
84     SKELTRIINTERSECT(va.pos, vb.pos, vc.pos)
85 }
86 
87 struct skelbihstack
88 {
89     skelbih::node *node;
90     float tmin, tmax;
91 };
92 
intersect(skelmodel::skelmeshgroup * m,skelmodel::skin * s,const vec & o,const vec & ray,const vec & invray,node * curnode,float tmin,float tmax)93 inline void skelbih::intersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const vec &o, const vec &ray, const vec &invray, node *curnode, float tmin, float tmax)
94 {
95     skelbihstack stack[128];
96     int stacksize = 0;
97     ivec order(ray.x>0 ? 0 : 1, ray.y>0 ? 0 : 1, ray.z>0 ? 0 : 1);
98     for(;;)
99     {
100         int axis = curnode->axis();
101         int nearidx = order[axis], faridx = nearidx^1;
102         float nearsplit = (curnode->split[nearidx] - o[axis])*invray[axis],
103               farsplit = (curnode->split[faridx] - o[axis])*invray[axis];
104 
105         if(nearsplit <= tmin)
106         {
107             if(farsplit < tmax)
108             {
109                 if(!curnode->isleaf(faridx))
110                 {
111                     curnode += curnode->childindex(faridx);
112                     tmin = max(tmin, farsplit);
113                     continue;
114                 }
115                 else if(triintersect(m, s, curnode->childindex(faridx), o, ray))
116                     tmax = min(tmax, skelmodel::intersectdist/skelmodel::intersectscale);
117             }
118         }
119         else if(curnode->isleaf(nearidx))
120         {
121             if(triintersect(m, s, curnode->childindex(nearidx), o, ray)) tmax = min(tmax, skelmodel::intersectdist/skelmodel::intersectscale);
122             if(farsplit < tmax)
123             {
124                 if(!curnode->isleaf(faridx))
125                 {
126                     curnode += curnode->childindex(faridx);
127                     tmin = max(tmin, farsplit);
128                     continue;
129                 }
130                 else if(triintersect(m, s, curnode->childindex(faridx), o, ray))
131                     tmax = min(tmax, skelmodel::intersectdist/skelmodel::intersectscale);
132             }
133         }
134         else
135         {
136             if(farsplit < tmax)
137             {
138                 if(!curnode->isleaf(faridx))
139                 {
140                     if(stacksize < int(sizeof(stack)/sizeof(stack[0])))
141                     {
142                         skelbihstack &save = stack[stacksize++];
143                         save.node = curnode + curnode->childindex(faridx);
144                         save.tmin = max(tmin, farsplit);
145                         save.tmax = tmax;
146                     }
147                     else
148                     {
149                         intersect(m, s, o, ray, invray, curnode + curnode->childindex(nearidx), tmin, tmax);
150                         curnode += curnode->childindex(faridx);
151                         tmin = max(tmin, farsplit);
152                     }
153                 }
154                 else if(triintersect(m, s, curnode->childindex(faridx), o, ray))
155                     tmax = min(tmax, skelmodel::intersectdist/skelmodel::intersectscale);
156             }
157             curnode += curnode->childindex(nearidx);
158             tmax = min(tmax, nearsplit);
159             continue;
160         }
161         if(stacksize <= 0) return;
162         skelbihstack &restore = stack[--stacksize];
163         curnode = restore.node;
164         tmin = restore.tmin;
165         tmax = restore.tmax;
166     }
167 }
168 
intersect(skelmodel::skelmeshgroup * m,skelmodel::skin * s,const vec & o,const vec & ray)169 void skelbih::intersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const vec &o, const vec &ray)
170 {
171     vec invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f);
172     float tmin, tmax;
173     float t1 = (bbmin.x - o.x)*invray.x,
174           t2 = (bbmax.x - o.x)*invray.x;
175     if(invray.x > 0) { tmin = t1; tmax = t2; } else { tmin = t2; tmax = t1; }
176     t1 = (bbmin.y - o.y)*invray.y;
177     t2 = (bbmax.y - o.y)*invray.y;
178     if(invray.y > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
179     t1 = (bbmin.z - o.z)*invray.z;
180     t2 = (bbmax.z - o.z)*invray.z;
181     if(invray.z > 0) { tmin = max(tmin, t1); tmax = min(tmax, t2); } else { tmin = max(tmin, t2); tmax = min(tmax, t1); }
182     tmax = min(tmax, skelmodel::intersectdist/skelmodel::intersectscale);
183     if(tmin >= tmax) return;
184 
185     if(nodes) intersect(m, s, o, ray, invray, nodes, tmin, tmax);
186     else triintersect(m, s, 0, o, ray);
187 }
188 
build(skelmodel::skelmeshgroup * m,ushort * indices,int numindices,const vec & vmin,const vec & vmax)189 void skelbih::build(skelmodel::skelmeshgroup *m, ushort *indices, int numindices, const vec &vmin, const vec &vmax)
190 {
191     int axis = 2;
192     loopk(2) if(vmax[k] - vmin[k] > vmax[axis] - vmin[axis]) axis = k;
193 
194     vec leftmin, leftmax, rightmin, rightmax;
195     float splitleft, splitright;
196     int left, right;
197     loopk(3)
198     {
199         leftmin = rightmin = vec(1e16f, 1e16f, 1e16f);
200         leftmax = rightmax = vec(-1e16f, -1e16f, -1e16f);
201         float split = 0.5f*(vmax[axis] + vmin[axis]);
202         for(left = 0, right = numindices, splitleft = SHRT_MIN, splitright = SHRT_MAX; left < right;)
203         {
204             tri &tri = tris[indices[left]];
205             skelmodel::skelmesh *tm = (skelmodel::skelmesh *)m->meshes[tri.mesh];
206             const vec &ta = tm->verts[tri.vert[0]].pos, &tb = tm->verts[tri.vert[1]].pos, &tc = tm->verts[tri.vert[2]].pos;
207             vec trimin = vec(ta).min(tb).min(tc), trimax = vec(ta).max(tb).max(tc);
208             float amin = trimin[axis], amax = trimax[axis];
209             if(max(split - amin, 0.0f) > max(amax - split, 0.0f))
210             {
211                 ++left;
212                 splitleft = max(splitleft, amax);
213                 leftmin.min(trimin);
214                 leftmax.max(trimax);
215             }
216             else
217             {
218                 --right;
219                 swap(indices[left], indices[right]);
220                 splitright = min(splitright, amin);
221                 rightmin.min(trimin);
222                 rightmax.max(trimax);
223             }
224         }
225         if(left > 0 && right < numindices) break;
226         axis = (axis+1)%3;
227     }
228 
229     if(!left || right==numindices)
230     {
231         leftmin = rightmin = vec(1e16f, 1e16f, 1e16f);
232         leftmax = rightmax = vec(-1e16f, -1e16f, -1e16f);
233         left = right = numindices/2;
234         splitleft = SHRT_MIN;
235         splitright = SHRT_MAX;
236         loopi(numindices)
237         {
238             tri &tri = tris[indices[i]];
239             skelmodel::skelmesh *tm = (skelmodel::skelmesh *)m->meshes[tri.mesh];
240             const vec &ta = tm->verts[tri.vert[0]].pos, &tb = tm->verts[tri.vert[1]].pos, &tc = tm->verts[tri.vert[2]].pos;
241             vec trimin = vec(ta).min(tb).min(tc), trimax = vec(ta).max(tb).max(tc);
242             if(i < left)
243             {
244                 splitleft = max(splitleft, trimax[axis]);
245                 leftmin.min(trimin);
246                 leftmax.max(trimax);
247             }
248             else
249             {
250                 splitright = min(splitright, trimin[axis]);
251                 rightmin.min(trimin);
252                 rightmax.max(trimax);
253             }
254         }
255     }
256 
257     int offset = numnodes++;
258     node &curnode = nodes[offset];
259     curnode.split[0] = short(ceil(splitleft));
260     curnode.split[1] = short(floor(splitright));
261 
262     if(left==1) curnode.child[0] = (axis<<14) | indices[0];
263     else
264     {
265         curnode.child[0] = (axis<<14) | (numnodes - offset);
266         build(m, indices, left, leftmin, leftmax);
267     }
268 
269     if(numindices-right==1) curnode.child[1] = (1<<15) | (left==1 ? 1<<14 : 0) | indices[right];
270     else
271     {
272         curnode.child[1] = (left==1 ? 1<<14 : 0) | (numnodes - offset);
273         build(m, &indices[right], numindices-right, rightmin, rightmax);
274     }
275 }
276 
skelbih(skelmodel::skelmeshgroup * m,int numtris,tri * tris)277 skelbih::skelbih(skelmodel::skelmeshgroup *m, int numtris, tri *tris)
278   : nodes(NULL), numnodes(0), tris(tris), bbmin(1e16f, 1e16f, 1e16f), bbmax(-1e16f, -1e16f, -1e16f)
279 {
280     loopi(numtris)
281     {
282         tri &tri = tris[i];
283         skelmodel::skelmesh *tm = (skelmodel::skelmesh *)m->meshes[tri.mesh];
284         const vec &ta = tm->verts[tri.vert[0]].pos, &tb = tm->verts[tri.vert[1]].pos, &tc = tm->verts[tri.vert[2]].pos;
285         bbmin.min(ta).min(tb).min(tc);
286         bbmax.max(ta).max(tb).max(tc);
287     }
288     if(numtris > 1)
289     {
290         nodes = new node[numtris];
291         ushort *indices = new ushort[numtris];
292         loopi(numtris) indices[i] = i;
293         build(m, indices, numtris, bbmin, bbmax);
294         delete[] indices;
295     }
296 }
297 
298 struct skelhitzone
299 {
300     typedef skelbih::tri tri;
301 
302     int numparents, numchildren;
303     skelhitzone **parents, **children;
304     vec center, animcenter;
305     float radius;
306     int visited;
307     union
308     {
309         int blend;
310         int numtris;
311     };
312     union
313     {
314         tri *tris;
315         skelbih *bih;
316     };
317 
skelhitzoneskelhitzone318     skelhitzone() : numparents(0), numchildren(0), parents(NULL), children(NULL), center(0, 0, 0), animcenter(0, 0, 0), radius(0), visited(-1)
319     {
320         blend = -1;
321         bih = NULL;
322     }
323 
~skelhitzoneskelhitzone324     ~skelhitzone()
325     {
326         if(!numchildren) { DELETEP(bih); }
327         else { DELETEA(tris); }
328     }
329 
330     static bool triintersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const dualquat *bdata1, const dualquat *bdata2, int numblends, const tri &t, const vec &o, const vec &ray);
331 
shellintersectskelhitzone332     bool shellintersect(const vec &o, const vec &ray)
333     {
334         vec c(animcenter);
335         c.sub(o);
336         float v = c.dot(ray), inside = radius*radius - c.squaredlen();
337         if(inside < 0 && v < 0) return false;
338         float d = inside + v*v;
339         if(d < 0) return false;
340         v -= skelmodel::intersectdist/skelmodel::intersectscale;
341         return v < 0 || d >= v*v;
342     }
343 
intersectskelhitzone344     void intersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const dualquat *bdata1, const dualquat *bdata2, int numblends, const vec &o, const vec &ray)
345     {
346         if(!numchildren)
347         {
348             if(bih)
349             {
350                 const dualquat &b = blend < numblends ? bdata2[blend] : bdata1[m->skel->bones[blend - numblends].interpindex];
351                 vec bo = b.transposedtransform(o), bray = b.transposedtransformnormal(ray);
352                 bih->intersect(m, s, bo, bray);
353             }
354         }
355         else if(shellintersect(o, ray))
356         {
357             loopi(numtris) triintersect(m, s, bdata1, bdata2, numblends, tris[i], o, ray);
358             loopi(numchildren) if(children[i]->visited != visited)
359             {
360                 children[i]->visited = visited;
361                 children[i]->intersect(m, s, bdata1, bdata2, numblends, o, ray);
362             }
363         }
364     }
365 
propagateskelhitzone366     void propagate(skelmodel::skelmeshgroup *m, const dualquat *bdata1, const dualquat *bdata2, int numblends)
367     {
368         if(!numchildren)
369         {
370             const dualquat &b = blend < numblends ? bdata2[blend] : bdata1[m->skel->bones[blend - numblends].interpindex];
371             animcenter = b.transform(center);
372         }
373         else
374         {
375             animcenter = children[numchildren-1]->animcenter;
376             radius = children[numchildren-1]->radius;
377             loopi(numchildren-1)
378             {
379                 skelhitzone *child = children[i];
380                 vec n = child->animcenter;
381                 n.sub(animcenter);
382                 float dist = n.magnitude();
383                 if(child->radius >= dist + radius)
384                 {
385                     animcenter = child->animcenter;
386                     radius = child->radius;
387                 }
388                 else if(radius < dist + child->radius)
389                 {
390                     float newradius = 0.5f*(radius + dist + child->radius);
391                     animcenter.add(n.mul((newradius - radius)/dist));
392                     radius = newradius;
393                 }
394             }
395         }
396     }
397 };
398 
triintersect(skelmodel::skelmeshgroup * m,skelmodel::skin * s,const dualquat * bdata1,const dualquat * bdata2,int numblends,const tri & t,const vec & o,const vec & ray)399 bool skelhitzone::triintersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const dualquat *bdata1, const dualquat *bdata2, int numblends, const tri &t, const vec &o, const vec &ray)
400 {
401     skelmodel::skelmesh *tm = (skelmodel::skelmesh *)m->meshes[t.mesh];
402     const skelmodel::vert &va = tm->verts[t.vert[0]], &vb = tm->verts[t.vert[1]], &vc = tm->verts[t.vert[2]];
403     vec a = (va.blend < numblends ? bdata2[va.blend] : bdata1[m->blendcombos[va.blend].interpbones[0]]).transform(va.pos),
404         b = (vb.blend < numblends ? bdata2[vb.blend] : bdata1[m->blendcombos[vb.blend].interpbones[0]]).transform(vb.pos),
405         c = (vc.blend < numblends ? bdata2[vc.blend] : bdata1[m->blendcombos[vc.blend].interpbones[0]]).transform(vc.pos);
406     SKELTRIINTERSECT(a, b, c);
407 }
408 
409 struct skelzonekey
410 {
411     int blend;
412     uchar bones[12];
413 
skelzonekeyskelzonekey414     skelzonekey() : blend(-1) { memset(bones, 0xFF, sizeof(bones)); }
skelzonekeyskelzonekey415     skelzonekey(int bone) : blend(INT_MAX) { bones[0] = bone; memset(&bones[1], 0xFF, sizeof(bones)-1); }
skelzonekeyskelzonekey416     skelzonekey(skelmodel::skelmesh *m, const skelmodel::tri &t)
417       : blend(-1)
418     {
419         memset(bones, 0xFF, sizeof(bones));
420         addbones(m, t);
421     }
422 
includesskelzonekey423     bool includes(const skelzonekey &o)
424     {
425         int j = 0;
426         loopi(sizeof(bones))
427         {
428             if(bones[i] > o.bones[j]) return false;
429             if(bones[i] == o.bones[j]) j++;
430         }
431         return j < (int)sizeof(bones) ? o.bones[j] == 0xFF : blend < 0 || blend == o.blend;
432     }
433 
subtractskelzonekey434     void subtract(const skelzonekey &o)
435     {
436         int len = 0, j = 0;
437         loopi(sizeof(bones))
438         {
439         retry:
440             if(j >= (int)sizeof(o.bones) || bones[i] < o.bones[j]) { bones[len++] = bones[i]; continue; }
441             if(bones[i] == o.bones[j]) { j++; continue; }
442             do j++; while(j < (int)sizeof(o.bones) && bones[i] > o.bones[j]);
443             goto retry;
444         }
445         memset(&bones[len], 0xFF, sizeof(bones) - len);
446     }
447 
hasboneskelzonekey448     bool hasbone(int n)
449     {
450         loopi(sizeof(bones))
451         {
452             if(bones[i] == n) return true;
453             if(bones[i] == 0xFF) break;
454         }
455         return false;
456     }
457 
numbonesskelzonekey458     int numbones()
459     {
460         loopi(sizeof(bones)) if(bones[i] == 0xFF) return i;
461         return sizeof(bones);
462     }
463 
addboneskelzonekey464     void addbone(int n)
465     {
466         loopi(sizeof(bones))
467         {
468             if(n <= bones[i])
469             {
470                 if(n < bones[i])
471                 {
472                     memmove(&bones[i+1], &bones[i], sizeof(bones) - (i+1));
473                     bones[i] = n;
474                 }
475                 return;
476             }
477         }
478     }
479 
addbonesskelzonekey480     void addbones(skelmodel::skelmesh *m, const skelmodel::tri &t)
481     {
482         skelmodel::skelmeshgroup *g = (skelmodel::skelmeshgroup *)m->group;
483         int b0 = m->verts[t.vert[0]].blend, b1 = m->verts[t.vert[1]].blend, b2 = m->verts[t.vert[1]].blend;
484         const skelmodel::blendcombo &c0 = g->blendcombos[b0];
485         loopi(4) if(c0.weights[i]) addbone(c0.bones[i]);
486         if(b0 != b1 || b0 != b2)
487         {
488             const skelmodel::blendcombo &c1 = g->blendcombos[b1];
489             loopi(4) if(c1.weights[i]) addbone(c1.bones[i]);
490             const skelmodel::blendcombo &c2 = g->blendcombos[b2];
491             loopi(4) if(c2.weights[i]) addbone(c2.bones[i]);
492         }
493         else blend = b0;
494     }
495 };
496 
497 struct skelzonebounds
498 {
499     int owner;
500     vec bbmin, bbmax;
501 
skelzoneboundsskelzonebounds502     skelzonebounds() : owner(-1), bbmin(1e16f, 1e16f, 1e16f), bbmax(-1e16f, -1e16f, -1e16f) {}
503 
addvertskelzonebounds504     void addvert(const vec &p)
505     {
506         bbmin.x = min(bbmin.x, p.x);
507         bbmin.y = min(bbmin.y, p.y);
508         bbmin.z = min(bbmin.z, p.z);
509         bbmax.x = max(bbmax.x, p.x);
510         bbmax.y = max(bbmax.y, p.y);
511         bbmax.z = max(bbmax.z, p.z);
512     }
513 
emptyskelzonebounds514     bool empty() const { return bbmin.x > bbmax.x; }
515 
calccenterskelzonebounds516     vec calccenter() const
517     {
518         return vec(bbmin).add(bbmax).mul(0.5f);
519     }
520 
calcradiusskelzonebounds521     float calcradius() const
522     {
523         return vec(bbmax).sub(bbmin).mul(0.5f).magnitude();
524     }
525 };
526 
527 struct skelzoneinfo
528 {
529     int index, parents, conflicts;
530     skelzonekey key;
531     vector<skelzoneinfo *> children;
532     vector<skelhitzone::tri> tris;
533 
skelzoneinfoskelzoneinfo534     skelzoneinfo() : index(-1), parents(0), conflicts(0) {}
skelzoneinfoskelzoneinfo535     skelzoneinfo(const skelzonekey &key) : index(-1), parents(0), conflicts(0), key(key) {}
536 };
537 
htcmp(const skelzonekey & x,const skelzoneinfo & y)538 static inline bool htcmp(const skelzonekey &x, const skelzoneinfo &y) { return !memcmp(x.bones, y.key.bones, sizeof(x.bones)) && (x.bones[1] == 0xFF || x.blend == y.key.blend); }
539 
hthash(const skelzonekey & k)540 static inline uint hthash(const skelzonekey &k)
541 {
542     union { uint i[3]; uchar b[12]; } conv;
543     memcpy(conv.b, k.bones, sizeof(conv.b));
544     return conv.i[0]^conv.i[1]^conv.i[2];
545 }
546 
547 struct skelhitdata
548 {
549     int numzones, rootzones, visited;
550     skelhitzone *zones;
551     skelhitzone **links;
552     skelhitzone::tri *tris;
553     int numblends;
554     skelmodel::blendcacheentry blendcache;
555 
skelhitdataskelhitdata556     skelhitdata() : numzones(0), rootzones(0), visited(0), zones(NULL), links(NULL), tris(NULL), numblends(0) {}
~skelhitdataskelhitdata557     ~skelhitdata()
558     {
559         DELETEA(zones);
560         DELETEA(links);
561         DELETEA(tris);
562         DELETEA(blendcache.bdata);
563     }
564 
565     uchar chooseid(skelmodel::skelmeshgroup *g, skelmodel::skelmesh *m, const skelmodel::tri &t, const uchar *ids);
566     void build(skelmodel::skelmeshgroup *g, const uchar *ids);
567 
cleanupskelhitdata568     void cleanup()
569     {
570         blendcache.owner = -1;
571     }
572 
propagateskelhitdata573     void propagate(skelmodel::skelmeshgroup *m, const dualquat *bdata1, dualquat *bdata2)
574     {
575         visited = 0;
576         loopi(numzones)
577         {
578             zones[i].visited = -1;
579             zones[i].propagate(m, bdata1, bdata2, numblends);
580         }
581     }
582 
intersectskelhitdata583     void intersect(skelmodel::skelmeshgroup *m, skelmodel::skin *s, const dualquat *bdata1, dualquat *bdata2, const vec &o, const vec &ray)
584     {
585         if(++visited < 0)
586         {
587             visited = 0;
588             loopi(numzones) zones[i].visited = -1;
589         }
590         for(int i = numzones - rootzones; i < numzones; i++)
591         {
592             zones[i].visited = visited;
593             zones[i].intersect(m, s, bdata1, bdata2, numblends, o, ray);
594         }
595     }
596 };
597 
cleanuphitdata()598 void skelmodel::skelmeshgroup::cleanuphitdata()
599 {
600     if(hitdata) hitdata->cleanup();
601 }
602 
deletehitdata()603 void skelmodel::skelmeshgroup::deletehitdata()
604 {
605     DELETEP(hitdata);
606 }
607 
intersect(skelhitdata * z,part * p,const skelmodel::skelcacheentry & sc,const vec & o,const vec & ray)608 void skelmodel::skelmeshgroup::intersect(skelhitdata *z, part *p, const skelmodel::skelcacheentry &sc, const vec &o, const vec &ray)
609 {
610     int owner = &sc - &skel->skelcache[0];
611     skelmodel::blendcacheentry &bc = z->blendcache;
612     if(bc.owner != owner || bc != sc)
613     {
614         bc.owner = owner;
615         bc.millis = lastmillis;
616         (animcacheentry &)bc = sc;
617         blendbones(sc.bdata, bc.bdata, blendcombos.getbuf(), z->numblends);
618         z->propagate(this, sc.bdata, bc.bdata);
619     }
620     z->intersect(this, p->skins.getbuf(), sc.bdata, bc.bdata, o, ray);
621 }
622 
chooseid(skelmodel::skelmeshgroup * g,skelmodel::skelmesh * m,const skelmodel::tri & t,const uchar * ids)623 uchar skelhitdata::chooseid(skelmodel::skelmeshgroup *g, skelmodel::skelmesh *m, const skelmodel::tri &t, const uchar *ids)
624 {
625     int numused = 0;
626     uchar used[12];
627     float weights[12];
628     loopk(3)
629     {
630         const skelmodel::vert &v = m->verts[t.vert[k]];
631         const skelmodel::blendcombo &c = g->blendcombos[v.blend];
632         loopl(4) if(c.weights[l])
633         {
634             uchar id = ids[c.bones[l]];
635             loopi(numused) if(used[i] == id) { weights[i] += c.weights[l]; goto nextbone; }
636             used[numused] = id;
637             weights[numused] = c.weights[l];
638             numused++;
639         nextbone:;
640         }
641     }
642     uchar bestid = 0xFF;
643     float bestweight = 0;
644     loopi(numused) if(weights[i] > bestweight || (weights[i] == bestweight && used[i] < bestid))
645     {
646         bestid = used[i];
647         bestweight = weights[i];
648     }
649     return bestid;
650 }
651 
build(skelmodel::skelmeshgroup * g,const uchar * ids)652 void skelhitdata::build(skelmodel::skelmeshgroup *g, const uchar *ids)
653 {
654     if(numzones) return;
655 
656     hashset<skelzoneinfo> infomap(256);
657     vector<skelzoneinfo *> info;
658     skelzonebounds *bounds = new skelzonebounds[g->skel->numbones];
659     numblends = g->blendcombos.length();
660     loopv(g->blendcombos) if(!g->blendcombos[i].weights[1]) { numblends = i; break; }
661     blendcache.bdata = numblends > 0 ? new dualquat[numblends] : NULL;
662     loopi(min(g->meshes.length(), 0x100))
663     {
664         skelmodel::skelmesh *m = (skelmodel::skelmesh *)g->meshes[i];
665         loopj(m->numtris)
666         {
667             const skelmodel::tri &t = m->tris[j];
668             loopk(3)
669             {
670                 const skelmodel::vert &v = m->verts[t.vert[k]];
671                 const skelmodel::blendcombo &c = g->blendcombos[v.blend];
672                 loopl(4) if(c.weights[l]) bounds[c.bones[l]].addvert(v.pos);
673             }
674             skelzonekey key(m, t);
675             skelzoneinfo &zi = infomap.access(key, key);
676             if(key.blend >= numblends && zi.index < 0)
677             {
678                 bounds[key.bones[0]].owner = zi.index = info.length();
679                 info.add(&zi);
680             }
681             skelhitzone::tri &zt = zi.tris.add();
682             zt.mesh = i;
683             zt.id = chooseid(g, m, t, ids);
684             memcpy(zt.vert, t.vert, sizeof(zt.vert));
685         }
686     }
687     loopi(g->skel->numbones)
688     {
689         if(bounds[i].empty() || bounds[i].owner >= 0) continue;
690         skelzonekey key(i);
691         skelzoneinfo &zi = infomap.access(key, key);
692         zi.index = info.length();
693         info.add(&zi);
694     }
695     int leafzones = info.length();
696     enumerate(infomap, skelzoneinfo, zi, { if(zi.index < 0) info.add(&zi); });
697     for(int i = leafzones; i < info.length(); i++)
698     {
699         skelzoneinfo &zi = *info[i];
700         if(zi.key.blend >= 0) continue;
701         loopvj(info) if(i != j && zi.key.includes(info[j]->key))
702         {
703             skelzoneinfo &zj = *info[j];
704             loopvk(zi.children)
705             {
706                 skelzoneinfo &zk = *zi.children[k];
707                 if(zk.key.includes(zj.key)) goto nextzone;
708                 if(zj.key.includes(zk.key))
709                 {
710                     zk.parents--;
711                     zj.parents++;
712                     zi.children[k] = &zj;
713                     while(++k < zi.children.length()) if(zj.key.includes(zi.children[k]->key)) { zi.children[k]->parents--; zi.children.removeunordered(k--); }
714                     goto nextzone;
715                 }
716             }
717             zj.parents++;
718             zi.children.add(&zj);
719         nextzone:;
720         }
721         skelzonekey deps = zi.key;
722         loopvj(zi.children)
723         {
724             skelzoneinfo &zj = *zi.children[j];
725             if(zj.key.blend < 0 || zj.key.blend >= numblends) deps.subtract(zj.key);
726         }
727         loopj(sizeof(deps.bones))
728         {
729             if(deps.bones[j]==0xFF) break;
730             skelzonekey dep(deps.bones[j]);
731             skelzoneinfo &zj = infomap.access(dep, dep);
732             zj.parents++;
733             zi.children.add(&zj);
734         }
735     }
736     for(int i = leafzones; i < info.length(); i++)
737     {
738         skelzoneinfo &zi = *info[i];
739         loopvj(zi.children)
740         {
741             skelzoneinfo &zj = *zi.children[j];
742             if(zj.tris.length() <= 2 && zj.parents == 1)
743             {
744                 zi.tris.put(zj.tris.getbuf(), zj.tris.length());
745                 zj.tris.setsize(0);
746                 if(zj.index < 0)
747                 {
748                     zj.parents = 0;
749                     zi.children.removeunordered(j--);
750                 }
751                 zi.children.put(zj.children.getbuf(), zj.children.length());
752                 zj.children.setsize(0);
753             }
754         }
755     }
756     int numlinks = 0, numtris = 0;
757     loopvrev(info)
758     {
759         skelzoneinfo &zi = *info[i];
760         if(zi.parents || zi.tris.empty()) info.removeunordered(i);
761         zi.conflicts = zi.parents;
762         numlinks += zi.parents + zi.children.length();
763         numtris += zi.tris.length();
764     }
765     rootzones = info.length();
766     loopv(info)
767     {
768         skelzoneinfo &zi = *info[i];
769         zi.index = i;
770         loopvj(zi.children)
771         {
772             skelzoneinfo &zj = *zi.children[j];
773             if(!--zj.conflicts) info.add(&zj);
774         }
775     }
776     numzones = info.length();
777     zones = new skelhitzone[numzones];
778     links = numlinks ? new skelhitzone *[numlinks] : NULL;
779     tris = new skelhitzone::tri[numtris];
780     skelhitzone **curlink = links;
781     skelhitzone::tri *curtris = tris;
782     loopi(numzones)
783     {
784         skelhitzone &z = zones[i];
785         skelzoneinfo &zi = *info[info.length()-1 - i];
786         memcpy(curtris, zi.tris.getbuf(), zi.tris.length()*sizeof(skelhitzone::tri));
787         if(zi.key.blend >= numblends)
788         {
789             z.blend = zi.key.bones[0] + numblends;
790             if(zi.tris.length()) z.bih = new skelbih(g, zi.tris.length(), curtris);
791             const skelzonebounds &b = bounds[zi.key.bones[0]];
792             z.center = b.calccenter();
793             z.radius = b.calcradius();
794         }
795         else if(zi.key.blend >= 0)
796         {
797             z.blend = zi.key.blend;
798             z.bih = new skelbih(g, zi.tris.length(), curtris);
799             z.center = z.bih->calccenter();
800             z.radius = z.bih->calcradius();
801         }
802         else
803         {
804             z.numtris = zi.tris.length();
805             z.tris = curtris;
806         }
807         curtris += zi.tris.length();
808         z.parents = curlink;
809         curlink += zi.parents;
810         z.numchildren = zi.children.length();
811         z.children = curlink;
812         loopvj(zi.children) z.children[j] = &zones[info.length()-1 - zi.children[j]->index];
813         curlink += zi.children.length();
814     }
815     loopi(numzones)
816     {
817         skelhitzone &z = zones[i];
818         loopj(z.numchildren) z.children[j]->parents[z.children[j]->numparents++] = &z;
819     }
820     delete[] bounds;
821 }
822 
buildhitdata(const uchar * hitzones)823 void skelmodel::skelmeshgroup::buildhitdata(const uchar *hitzones)
824 {
825     if(hitdata) return;
826     hitdata = new skelhitdata;
827     hitdata->build(this, hitzones);
828 }
829 
830