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