1 VAR(IDF_PERSIST, gpuskel, 0, 1, 1); 2 3 VAR(0, maxskelanimdata, 1, 192, 0); 4 5 #define BONEMASK_NOT 0x8000 6 #define BONEMASK_END 0xFFFF 7 #define BONEMASK_BONE 0x7FFF 8 9 struct skelhitdata; 10 11 struct skelmodel : animmodel 12 { 13 struct vert { vec pos, norm; vec2 tc; quat tangent; int blend, interpindex; }; 14 struct vvert { vec pos; hvec2 tc; squat tangent; }; 15 struct vvertg { hvec4 pos; hvec2 tc; squat tangent; }; 16 struct vvertgw : vvertg { uchar weights[4]; uchar bones[4]; }; 17 struct vvertgc : vvertg { bvec4 col; }; 18 struct vvertgwc : vvertgw { bvec4 col; }; 19 struct tri { ushort vert[3]; }; 20 21 struct blendcombo 22 { 23 int uses, interpindex; 24 float weights[4]; 25 uchar bones[4], interpbones[4]; 26 blendcomboskelmodel::blendcombo27 blendcombo() : uses(1) 28 { 29 } 30 31 bool operator==(const blendcombo &c) const 32 { 33 loopk(4) if(bones[k] != c.bones[k]) return false; 34 loopk(4) if(weights[k] != c.weights[k]) return false; 35 return true; 36 } 37 sizeskelmodel::blendcombo38 int size() const 39 { 40 int i = 1; 41 while(i < 4 && weights[i]) i++; 42 return i; 43 } 44 sortcmpskelmodel::blendcombo45 static bool sortcmp(const blendcombo &x, const blendcombo &y) 46 { 47 loopi(4) 48 { 49 if(x.weights[i]) 50 { 51 if(!y.weights[i]) return true; 52 } 53 else if(y.weights[i]) return false; 54 else break; 55 } 56 return false; 57 } 58 addweightskelmodel::blendcombo59 int addweight(int sorted, float weight, int bone) 60 { 61 if(weight <= 1e-3f) return sorted; 62 loopk(sorted) if(weight > weights[k]) 63 { 64 for(int l = min(sorted-1, 2); l >= k; l--) 65 { 66 weights[l+1] = weights[l]; 67 bones[l+1] = bones[l]; 68 } 69 weights[k] = weight; 70 bones[k] = bone; 71 return sorted<4 ? sorted+1 : sorted; 72 } 73 if(sorted>=4) return sorted; 74 weights[sorted] = weight; 75 bones[sorted] = bone; 76 return sorted+1; 77 } 78 finalizeskelmodel::blendcombo79 void finalize(int sorted) 80 { 81 loopj(4-sorted) { weights[sorted+j] = 0; bones[sorted+j] = 0; } 82 if(sorted <= 0) return; 83 float total = 0; 84 loopj(sorted) total += weights[j]; 85 total = 1.0f/total; 86 loopj(sorted) weights[j] *= total; 87 } 88 89 template<class T> serializeskelmodel::blendcombo90 void serialize(T &v) 91 { 92 if(interpindex >= 0) 93 { 94 v.weights[0] = 255; 95 loopk(3) v.weights[k+1] = 0; 96 v.bones[0] = 2*interpindex; 97 loopk(3) v.bones[k+1] = v.bones[0]; 98 } 99 else 100 { 101 int total = 0; 102 loopk(4) total += (v.weights[k] = uchar(0.5f + weights[k]*255)); 103 while(total > 255) 104 { 105 loopk(4) if(v.weights[k] > 0 && total > 255) { v.weights[k]--; total--; } 106 } 107 while(total < 255) 108 { 109 loopk(4) if(v.weights[k] < 255 && total < 255) { v.weights[k]++; total++; } 110 } 111 loopk(4) v.bones[k] = 2*interpbones[k]; 112 } 113 } 114 }; 115 116 117 struct animcacheentry 118 { 119 animstate as[MAXANIMPARTS]; 120 float pitch; 121 int millis; 122 uchar *partmask; 123 ragdolldata *ragdoll; 124 animcacheentryskelmodel::animcacheentry125 animcacheentry() : ragdoll(NULL) 126 { 127 loopk(MAXANIMPARTS) as[k].cur.fr1 = as[k].prev.fr1 = -1; 128 } 129 130 bool operator==(const animcacheentry &c) const 131 { 132 loopi(MAXANIMPARTS) if(as[i]!=c.as[i]) return false; 133 return pitch==c.pitch && partmask==c.partmask && ragdoll==c.ragdoll && (!ragdoll || min(millis, c.millis) >= ragdoll->lastmove); 134 } 135 136 bool operator!=(const animcacheentry &c) const 137 { 138 return !operator==(c); 139 } 140 }; 141 142 struct vbocacheentry : animcacheentry 143 { 144 GLuint vbuf; 145 int owner; 146 vbocacheentryskelmodel::vbocacheentry147 vbocacheentry() : vbuf(0), owner(-1) {} 148 }; 149 150 struct skelcacheentry : animcacheentry 151 { 152 dualquat *bdata; 153 int version; 154 skelcacheentryskelmodel::skelcacheentry155 skelcacheentry() : bdata(NULL), version(-1) {} 156 nextversionskelmodel::skelcacheentry157 void nextversion() 158 { 159 version = Shader::uniformlocversion(); 160 } 161 }; 162 163 struct blendcacheentry : skelcacheentry 164 { 165 int owner; 166 blendcacheentryskelmodel::blendcacheentry167 blendcacheentry() : owner(-1) {} 168 }; 169 170 struct skelmeshgroup; 171 172 struct skelmesh : mesh 173 { 174 vert *verts; 175 tri *tris; 176 bvec4 *vcolors; 177 int numverts, numtris, maxweights; 178 179 int voffset, eoffset, elen; 180 ushort minvert, maxvert; 181 skelmeshskelmodel::skelmesh182 skelmesh() : verts(NULL), tris(NULL), vcolors(NULL), numverts(0), numtris(0), maxweights(0) 183 { 184 } 185 ~skelmeshskelmodel::skelmesh186 virtual ~skelmesh() 187 { 188 DELETEA(verts); 189 DELETEA(tris); 190 DELETEA(vcolors); 191 } 192 addblendcomboskelmodel::skelmesh193 int addblendcombo(const blendcombo &c) 194 { 195 maxweights = max(maxweights, c.size()); 196 return ((skelmeshgroup *)group)->addblendcombo(c); 197 } 198 199 void smoothnorms(float limit = 0, bool areaweight = true) 200 { 201 mesh::smoothnorms(verts, numverts, tris, numtris, limit, areaweight); 202 } 203 204 void buildnorms(bool areaweight = true) 205 { 206 mesh::buildnorms(verts, numverts, tris, numtris, areaweight); 207 } 208 209 void calctangents(bool areaweight = true) 210 { 211 mesh::calctangents(verts, verts, numverts, tris, numtris, areaweight); 212 } 213 calcbbskelmodel::skelmesh214 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) 215 { 216 loopj(numverts) 217 { 218 vec v = m.transform(verts[j].pos); 219 bbmin.min(v); 220 bbmax.max(v); 221 } 222 } 223 genBIHskelmodel::skelmesh224 void genBIH(BIH::mesh &m) 225 { 226 m.tris = (const BIH::tri *)tris; 227 m.numtris = numtris; 228 m.pos = (const uchar *)&verts->pos; 229 m.posstride = sizeof(vert); 230 m.tc = (const uchar *)&verts->tc; 231 m.tcstride = sizeof(vert); 232 } 233 genshadowmeshskelmodel::skelmesh234 void genshadowmesh(vector<triangle> &out, const matrix4x3 &m) 235 { 236 loopj(numtris) 237 { 238 triangle &t = out.add(); 239 t.a = m.transform(verts[tris[j].vert[0]].pos); 240 t.b = m.transform(verts[tris[j].vert[1]].pos); 241 t.c = m.transform(verts[tris[j].vert[2]].pos); 242 } 243 } 244 assignvertskelmodel::skelmesh245 inline void assignvert(vvertg &vv, int j, vert &v, blendcombo &c) 246 { 247 vv.pos = hvec4(v.pos, 1); 248 vv.tc = v.tc; 249 vv.tangent = v.tangent; 250 } 251 assignvertskelmodel::skelmesh252 inline void assignvert(vvertgc &vv, int j, vert &v, blendcombo &c) 253 { 254 vv.pos = hvec4(v.pos, 1); 255 vv.tc = v.tc; 256 vv.tangent = v.tangent; 257 vv.col = vcolors ? vcolors[j] : bvec4(255, 255, 255, 255); 258 } 259 assignvertskelmodel::skelmesh260 inline void assignvert(vvertgw &vv, int j, vert &v, blendcombo &c) 261 { 262 vv.pos = hvec4(v.pos, 1); 263 vv.tc = v.tc; 264 vv.tangent = v.tangent; 265 c.serialize(vv); 266 } 267 assignvertskelmodel::skelmesh268 inline void assignvert(vvertgwc &vv, int j, vert &v, blendcombo &c) 269 { 270 vv.pos = hvec4(v.pos, 1); 271 vv.tc = v.tc; 272 vv.tangent = v.tangent; 273 c.serialize(vv); 274 vv.col = vcolors ? vcolors[j] : bvec4(255, 255, 255, 255); 275 } 276 277 template<class T> genvboskelmodel::skelmesh278 int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts) 279 { 280 voffset = offset; 281 eoffset = idxs.length(); 282 loopi(numverts) 283 { 284 vert &v = verts[i]; 285 assignvert(vverts.add(), i, v, ((skelmeshgroup *)group)->blendcombos[v.blend]); 286 } 287 loopi(numtris) loopj(3) idxs.add(voffset + tris[i].vert[j]); 288 elen = idxs.length()-eoffset; 289 minvert = voffset; 290 maxvert = voffset + numverts-1; 291 return numverts; 292 } 293 294 template<class T> genvboskelmodel::skelmesh295 int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts, int *htdata, int htlen) 296 { 297 voffset = offset; 298 eoffset = idxs.length(); 299 minvert = 0xFFFF; 300 loopi(numtris) 301 { 302 tri &t = tris[i]; 303 loopj(3) 304 { 305 int index = t.vert[j]; 306 vert &v = verts[index]; 307 T vv; 308 assignvert(vv, index, v, ((skelmeshgroup *)group)->blendcombos[v.blend]); 309 int htidx = hthash(v.pos)&(htlen-1); 310 loopk(htlen) 311 { 312 int &vidx = htdata[(htidx+k)&(htlen-1)]; 313 if(vidx < 0) { vidx = idxs.add(ushort(vverts.length())); vverts.add(vv); break; } 314 else if(!memcmp(&vverts[vidx], &vv, sizeof(vv))) { minvert = min(minvert, idxs.add(ushort(vidx))); break; } 315 } 316 } 317 } 318 elen = idxs.length()-eoffset; 319 minvert = min(minvert, ushort(voffset)); 320 maxvert = max(minvert, ushort(vverts.length()-1)); 321 return vverts.length()-voffset; 322 } 323 genvboskelmodel::skelmesh324 int genvbo(vector<ushort> &idxs, int offset) 325 { 326 loopi(numverts) verts[i].interpindex = ((skelmeshgroup *)group)->remapblend(verts[i].blend); 327 328 voffset = offset; 329 eoffset = idxs.length(); 330 loopi(numtris) 331 { 332 tri &t = tris[i]; 333 loopj(3) idxs.add(voffset+t.vert[j]); 334 } 335 minvert = voffset; 336 maxvert = voffset + numverts-1; 337 elen = idxs.length()-eoffset; 338 return numverts; 339 } 340 341 template<class T> fillvertskelmodel::skelmesh342 static inline void fillvert(T &vv, int j, vert &v) 343 { 344 vv.tc = v.tc; 345 } 346 347 template<class T> fillvertsskelmodel::skelmesh348 void fillverts(T *vdata) 349 { 350 vdata += voffset; 351 loopi(numverts) fillvert(vdata[i], i, verts[i]); 352 } 353 354 template<class T> interpvertsskelmodel::skelmesh355 void interpverts(const dualquat * RESTRICT bdata1, const dualquat * RESTRICT bdata2, T * RESTRICT vdata, skin &s) 356 { 357 const int blendoffset = ((skelmeshgroup *)group)->skel->numgpubones; 358 bdata2 -= blendoffset; 359 vdata += voffset; 360 loopi(numverts) 361 { 362 const vert &src = verts[i]; 363 T &dst = vdata[i]; 364 const dualquat &b = (src.interpindex < blendoffset ? bdata1 : bdata2)[src.interpindex]; 365 dst.pos = b.transform(src.pos); 366 quat q = b.transform(src.tangent); 367 fixqtangent(q, src.tangent.w); 368 dst.tangent = q; 369 } 370 } 371 setshaderskelmodel::skelmesh372 void setshader(Shader *s, int row) 373 { 374 skelmeshgroup *g = (skelmeshgroup *)group; 375 if(row) s->setvariant(g->skel->usegpuskel ? min(maxweights, g->vweights) : 0, row); 376 else if(g->skel->usegpuskel) s->setvariant(min(maxweights, g->vweights)-1, 0); 377 else s->set(); 378 } 379 renderskelmodel::skelmesh380 void render(const animstate *as, skin &s, vbocacheentry &vc) 381 { 382 if(!Shader::lastshader) return; 383 glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((skelmeshgroup *)group)->edata[eoffset]); 384 glde++; 385 xtravertsva += numverts; 386 } 387 }; 388 389 390 struct tag 391 { 392 char *name; 393 int bone; 394 matrix4x3 matrix; 395 tagskelmodel::tag396 tag() : name(NULL) {} ~tagskelmodel::tag397 ~tag() { DELETEA(name); } 398 }; 399 400 struct skelanimspec 401 { 402 char *name; 403 int frame, range; 404 skelanimspecskelmodel::skelanimspec405 skelanimspec() : name(NULL), frame(0), range(0) {} ~skelanimspecskelmodel::skelanimspec406 ~skelanimspec() 407 { 408 DELETEA(name); 409 } 410 }; 411 412 struct boneinfo 413 { 414 const char *name; 415 int parent, children, next, group, scheduled, interpindex, interpparent, ragdollindex, correctindex; 416 float pitchscale, pitchoffset, pitchmin, pitchmax; 417 dualquat base, invbase; 418 boneinfoskelmodel::boneinfo419 boneinfo() : name(NULL), parent(-1), children(-1), next(-1), group(INT_MAX), scheduled(-1), interpindex(-1), interpparent(-1), ragdollindex(-1), correctindex(-1), pitchscale(0), pitchoffset(0), pitchmin(0), pitchmax(0) {} ~boneinfoskelmodel::boneinfo420 ~boneinfo() 421 { 422 DELETEA(name); 423 } 424 }; 425 426 struct antipode 427 { 428 int parent, child; 429 antipodeskelmodel::antipode430 antipode(int parent, int child) : parent(parent), child(child) {} 431 }; 432 433 struct pitchdep 434 { 435 int bone, parent; 436 dualquat pose; 437 }; 438 439 struct pitchtarget 440 { 441 int bone, frame, corrects, deps; 442 float pitchmin, pitchmax, deviated; 443 dualquat pose; 444 }; 445 446 struct pitchcorrect 447 { 448 int bone, target, parent; 449 float pitchmin, pitchmax, pitchscale, pitchangle, pitchtotal; 450 pitchcorrectskelmodel::pitchcorrect451 pitchcorrect() : parent(-1), pitchangle(0), pitchtotal(0) {} 452 }; 453 454 struct skeleton 455 { 456 char *name; 457 int shared; 458 vector<skelmeshgroup *> users; 459 boneinfo *bones; 460 int numbones, numinterpbones, numgpubones, numframes; 461 dualquat *framebones; 462 vector<skelanimspec> skelanims; 463 vector<tag> tags; 464 vector<antipode> antipodes; 465 ragdollskel *ragdoll; 466 vector<pitchdep> pitchdeps; 467 vector<pitchtarget> pitchtargets; 468 vector<pitchcorrect> pitchcorrects; 469 470 bool usegpuskel; 471 vector<skelcacheentry> skelcache; 472 hashtable<GLuint, int> blendoffsets; 473 skeletonskelmodel::skeleton474 skeleton() : name(NULL), shared(0), bones(NULL), numbones(0), numinterpbones(0), numgpubones(0), numframes(0), framebones(NULL), ragdoll(NULL), usegpuskel(false), blendoffsets(32) 475 { 476 } 477 ~skeletonskelmodel::skeleton478 ~skeleton() 479 { 480 DELETEA(name); 481 DELETEA(bones); 482 DELETEA(framebones); 483 DELETEP(ragdoll); 484 loopv(skelcache) 485 { 486 DELETEA(skelcache[i].bdata); 487 } 488 } 489 490 skelanimspec *findskelanim(const char *name, char sep = '\0') 491 { 492 int len = sep ? strlen(name) : 0; loopvskelmodel::skeleton493 loopv(skelanims) 494 { 495 if(skelanims[i].name) 496 { 497 if(sep) 498 { 499 const char *end = strchr(skelanims[i].name, ':'); 500 if(end && end - skelanims[i].name == len && !memcmp(name, skelanims[i].name, len)) return &skelanims[i]; 501 } 502 if(!strcmp(name, skelanims[i].name)) return &skelanims[i]; 503 } 504 } 505 return NULL; 506 } 507 addskelanimskelmodel::skeleton508 skelanimspec &addskelanim(const char *name) 509 { 510 skelanimspec &sa = skelanims.add(); 511 sa.name = name ? newstring(name) : NULL; 512 return sa; 513 } 514 findboneskelmodel::skeleton515 int findbone(const char *name) 516 { 517 loopi(numbones) if(cubematchstr(bones[i].name, name)) return i; 518 return -1; 519 } 520 findbonesskelmodel::skeleton521 int findbones(const char *name, vector<int> &elems) 522 { 523 int num = 0; 524 loopi(numbones) if(cubepattern(bones[i].name, name) >= 0) 525 { 526 elems.add(i); 527 num++; 528 } 529 return num; 530 } 531 findtagskelmodel::skeleton532 int findtag(const char *name) 533 { 534 loopv(tags) if(cubematchstr(tags[i].name, name)) return i; 535 return -1; 536 } 537 findtagsskelmodel::skeleton538 int findtags(const char *name, vector<int> &elems) 539 { 540 int num = 0; 541 loopv(tags) if(cubepattern(tags[i].name, name) >= 0) 542 { 543 elems.add(i); 544 num++; 545 } 546 return num; 547 } 548 addtagskelmodel::skeleton549 bool addtag(const char *name, int bone, const matrix4x3 &matrix) 550 { 551 int idx = findtag(name); 552 if(idx >= 0) 553 { 554 if(!testtags) return false; 555 tag &t = tags[idx]; 556 t.bone = bone; 557 t.matrix = matrix; 558 } 559 else 560 { 561 tag &t = tags.add(); 562 t.name = newstring(name); 563 t.bone = bone; 564 t.matrix = matrix; 565 } 566 return true; 567 } 568 calcantipodesskelmodel::skeleton569 void calcantipodes() 570 { 571 antipodes.shrink(0); 572 vector<int> schedule; 573 loopi(numbones) 574 { 575 if(bones[i].group >= numbones) 576 { 577 bones[i].scheduled = schedule.length(); 578 schedule.add(i); 579 } 580 else bones[i].scheduled = -1; 581 } 582 loopv(schedule) 583 { 584 int bone = schedule[i]; 585 const boneinfo &info = bones[bone]; 586 loopj(numbones) if(abs(bones[j].group) == bone && bones[j].scheduled < 0) 587 { 588 antipodes.add(antipode(info.interpindex, bones[j].interpindex)); 589 bones[j].scheduled = schedule.length(); 590 schedule.add(j); 591 } 592 if(i + 1 == schedule.length()) 593 { 594 int conflict = INT_MAX; 595 loopj(numbones) if(bones[j].group < numbones && bones[j].scheduled < 0) conflict = min(conflict, abs(bones[j].group)); 596 if(conflict < numbones) 597 { 598 bones[conflict].scheduled = schedule.length(); 599 schedule.add(conflict); 600 } 601 } 602 } 603 } 604 remapbonesskelmodel::skeleton605 void remapbones() 606 { 607 loopi(numbones) 608 { 609 boneinfo &info = bones[i]; 610 info.interpindex = -1; 611 info.ragdollindex = -1; 612 } 613 numgpubones = 0; 614 loopv(users) 615 { 616 skelmeshgroup *group = users[i]; 617 loopvj(group->blendcombos) 618 { 619 blendcombo &c = group->blendcombos[j]; 620 loopk(4) 621 { 622 if(!c.weights[k]) { c.interpbones[k] = k > 0 ? c.interpbones[k-1] : 0; continue; } 623 boneinfo &info = bones[c.bones[k]]; 624 if(info.interpindex < 0) info.interpindex = numgpubones++; 625 c.interpbones[k] = info.interpindex; 626 if(info.group < 0) continue; 627 loopl(4) 628 { 629 if(!c.weights[l]) break; 630 if(l == k) continue; 631 int parent = c.bones[l]; 632 if(info.parent == parent || (info.parent >= 0 && info.parent == bones[parent].parent)) { info.group = -info.parent; break; } 633 if(info.group <= parent) continue; 634 int child = c.bones[k]; 635 while(parent > child) parent = bones[parent].parent; 636 if(parent != child) info.group = c.bones[l]; 637 } 638 } 639 } 640 } 641 numinterpbones = numgpubones; 642 loopv(tags) 643 { 644 boneinfo &info = bones[tags[i].bone]; 645 if(info.interpindex < 0) info.interpindex = numinterpbones++; 646 } 647 if(ragdoll) 648 { 649 loopv(ragdoll->joints) 650 { 651 boneinfo &info = bones[ragdoll->joints[i].bone]; 652 if(info.interpindex < 0) info.interpindex = numinterpbones++; 653 info.ragdollindex = i; 654 } 655 } 656 loopi(numbones) 657 { 658 boneinfo &info = bones[i]; 659 if(info.interpindex < 0) continue; 660 for(int parent = info.parent; parent >= 0 && bones[parent].interpindex < 0; parent = bones[parent].parent) 661 bones[parent].interpindex = numinterpbones++; 662 } 663 loopi(numbones) 664 { 665 boneinfo &info = bones[i]; 666 if(info.interpindex < 0) continue; 667 info.interpparent = info.parent >= 0 ? bones[info.parent].interpindex : -1; 668 } 669 if(ragdoll) 670 { 671 loopi(numbones) 672 { 673 boneinfo &info = bones[i]; 674 if(info.interpindex < 0 || info.ragdollindex >= 0) continue; 675 for(int parent = info.parent; parent >= 0; parent = bones[parent].parent) 676 { 677 if(bones[parent].ragdollindex >= 0) { ragdoll->addreljoint(i, bones[parent].ragdollindex); break; } 678 } 679 } 680 } 681 calcantipodes(); 682 } 683 684 addpitchdepskelmodel::skeleton685 void addpitchdep(int bone, int frame) 686 { 687 for(; bone >= 0; bone = bones[bone].parent) 688 { 689 int pos = pitchdeps.length(); 690 loopvj(pitchdeps) if(bone <= pitchdeps[j].bone) 691 { 692 if(bone == pitchdeps[j].bone) goto nextbone; 693 pos = j; 694 break; 695 } 696 { 697 pitchdep d; 698 d.bone = bone; 699 d.parent = -1; 700 d.pose = framebones[frame*numbones + bone]; 701 pitchdeps.insert(pos, d); 702 } 703 nextbone:; 704 } 705 } 706 findpitchdepskelmodel::skeleton707 int findpitchdep(int bone) 708 { 709 loopv(pitchdeps) if(bone <= pitchdeps[i].bone) return bone == pitchdeps[i].bone ? i : -1; 710 return -1; 711 } 712 findpitchcorrectskelmodel::skeleton713 int findpitchcorrect(int bone) 714 { 715 loopv(pitchcorrects) if(bone <= pitchcorrects[i].bone) return bone == pitchcorrects[i].bone ? i : -1; 716 return -1; 717 } 718 initpitchdepsskelmodel::skeleton719 void initpitchdeps() 720 { 721 pitchdeps.setsize(0); 722 if(pitchtargets.empty()) return; 723 loopv(pitchtargets) 724 { 725 pitchtarget &t = pitchtargets[i]; 726 t.deps = -1; 727 addpitchdep(t.bone, t.frame); 728 } 729 loopv(pitchdeps) 730 { 731 pitchdep &d = pitchdeps[i]; 732 int parent = bones[d.bone].parent; 733 if(parent >= 0) 734 { 735 int j = findpitchdep(parent); 736 if(j >= 0) 737 { 738 d.parent = j; 739 d.pose.mul(pitchdeps[j].pose, dualquat(d.pose)); 740 } 741 } 742 } 743 loopv(pitchtargets) 744 { 745 pitchtarget &t = pitchtargets[i]; 746 int j = findpitchdep(t.bone); 747 if(j >= 0) 748 { 749 t.deps = j; 750 t.pose = pitchdeps[j].pose; 751 } 752 t.corrects = -1; 753 for(int parent = t.bone; parent >= 0; parent = bones[parent].parent) 754 { 755 t.corrects = findpitchcorrect(parent); 756 if(t.corrects >= 0) break; 757 } 758 } 759 loopv(pitchcorrects) 760 { 761 pitchcorrect &c = pitchcorrects[i]; 762 bones[c.bone].correctindex = i; 763 c.parent = -1; 764 for(int parent = c.bone;;) 765 { 766 parent = bones[parent].parent; 767 if(parent < 0) break; 768 c.parent = findpitchcorrect(parent); 769 if(c.parent >= 0) break; 770 } 771 } 772 } 773 optimizeskelmodel::skeleton774 void optimize() 775 { 776 cleanup(); 777 if(ragdoll) ragdoll->setup(); 778 remapbones(); 779 initpitchdeps(); 780 } 781 expandbonemaskskelmodel::skeleton782 void expandbonemask(uchar *expansion, int bone, int val) 783 { 784 expansion[bone] = val; 785 bone = bones[bone].children; 786 while(bone>=0) { expandbonemask(expansion, bone, val); bone = bones[bone].next; } 787 } 788 applybonemaskskelmodel::skeleton789 void applybonemask(ushort *mask, uchar *partmask, int partindex) 790 { 791 if(!mask || *mask==BONEMASK_END) return; 792 uchar *expansion = new uchar[numbones]; 793 memset(expansion, *mask&BONEMASK_NOT ? 1 : 0, numbones); 794 while(*mask!=BONEMASK_END) 795 { 796 expandbonemask(expansion, *mask&BONEMASK_BONE, *mask&BONEMASK_NOT ? 0 : 1); 797 mask++; 798 } 799 loopi(numbones) if(expansion[i]) partmask[i] = partindex; 800 delete[] expansion; 801 } 802 linkchildrenskelmodel::skeleton803 void linkchildren() 804 { 805 loopi(numbones) 806 { 807 boneinfo &b = bones[i]; 808 b.children = -1; 809 if(b.parent<0) b.next = -1; 810 else 811 { 812 b.next = bones[b.parent].children; 813 bones[b.parent].children = i; 814 } 815 } 816 } 817 availgpubonesskelmodel::skeleton818 int availgpubones() const { return min(maxvsuniforms, maxskelanimdata) / 2; } gpuaccelerateskelmodel::skeleton819 bool gpuaccelerate() const { return numframes && gpuskel && numgpubones<=availgpubones(); } 820 calcdeviationskelmodel::skeleton821 float calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2) 822 { 823 vec forward1 = pose1.transformnormal(forward).project(axis).normalize(), 824 forward2 = pose2.transformnormal(forward).project(axis).normalize(), 825 daxis = vec().cross(forward1, forward2); 826 float dx = clamp(forward1.dot(forward2), -1.0f, 1.0f), dy = clamp(daxis.magnitude(), -1.0f, 1.0f); 827 if(daxis.dot(axis) < 0) dy = -dy; 828 return atan2f(dy, dx)/RAD; 829 } 830 calcpitchcorrectsskelmodel::skeleton831 void calcpitchcorrects(float pitch, const vec &axis, const vec &forward) 832 { 833 loopv(pitchtargets) 834 { 835 pitchtarget &t = pitchtargets[i]; 836 t.deviated = calcdeviation(axis, forward, t.pose, pitchdeps[t.deps].pose); 837 } 838 loopv(pitchcorrects) 839 { 840 pitchcorrect &c = pitchcorrects[i]; 841 c.pitchangle = c.pitchtotal = 0; 842 } 843 loopvj(pitchtargets) 844 { 845 pitchtarget &t = pitchtargets[j]; 846 float tpitch = pitch - t.deviated; 847 for(int parent = t.corrects; parent >= 0; parent = pitchcorrects[parent].parent) 848 tpitch -= pitchcorrects[parent].pitchangle; 849 if(t.pitchmin || t.pitchmax) tpitch = clamp(tpitch, t.pitchmin, t.pitchmax); 850 loopv(pitchcorrects) 851 { 852 pitchcorrect &c = pitchcorrects[i]; 853 if(c.target != j) continue; 854 float total = c.parent >= 0 ? pitchcorrects[c.parent].pitchtotal : 0, 855 avail = tpitch - total, 856 used = tpitch*c.pitchscale; 857 if(c.pitchmin || c.pitchmax) 858 { 859 if(used < 0) used = clamp(c.pitchmin, used, 0.0f); 860 else used = clamp(c.pitchmax, 0.0f, used); 861 } 862 if(used < 0) used = clamp(avail, used, 0.0f); 863 else used = clamp(avail, 0.0f, used); 864 c.pitchangle = used; 865 c.pitchtotal = used + total; 866 } 867 } 868 } 869 870 #define INTERPBONE(bone) \ 871 const animstate &s = as[partmask[bone]]; \ 872 const framedata &f = partframes[partmask[bone]]; \ 873 dualquat d; \ 874 (d = f.fr1[bone]).mul((1-s.cur.t)*s.interp); \ 875 d.accumulate(f.fr2[bone], s.cur.t*s.interp); \ 876 if(s.interp<1) \ 877 { \ 878 d.accumulate(f.pfr1[bone], (1-s.prev.t)*(1-s.interp)); \ 879 d.accumulate(f.pfr2[bone], s.prev.t*(1-s.interp)); \ 880 } 881 interpbonesskelmodel::skeleton882 void interpbones(const animstate *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc) 883 { 884 if(!sc.bdata) sc.bdata = new dualquat[numinterpbones]; 885 sc.nextversion(); 886 struct framedata 887 { 888 const dualquat *fr1, *fr2, *pfr1, *pfr2; 889 } partframes[MAXANIMPARTS]; 890 loopi(numanimparts) 891 { 892 partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones]; 893 partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones]; 894 if(as[i].interp<1) 895 { 896 partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones]; 897 partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones]; 898 } 899 } 900 loopv(pitchdeps) 901 { 902 pitchdep &p = pitchdeps[i]; 903 INTERPBONE(p.bone); 904 d.normalize(); 905 if(p.parent >= 0) p.pose.mul(pitchdeps[p.parent].pose, d); 906 else p.pose = d; 907 } 908 calcpitchcorrects(pitch, axis, forward); 909 loopi(numbones) if(bones[i].interpindex>=0) 910 { 911 INTERPBONE(i); 912 d.normalize(); 913 const boneinfo &b = bones[i]; 914 if(b.interpparent<0) sc.bdata[b.interpindex] = d; 915 else sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d); 916 float angle; 917 if(b.pitchscale) { angle = b.pitchscale*pitch + b.pitchoffset; if(b.pitchmin || b.pitchmax) angle = clamp(angle, b.pitchmin, b.pitchmax); } 918 else if(b.correctindex >= 0) angle = pitchcorrects[b.correctindex].pitchangle; 919 else continue; 920 if(as->cur.anim&ANIM_NOPITCH || (as->interp < 1 && as->prev.anim&ANIM_NOPITCH)) 921 angle *= (as->cur.anim&ANIM_NOPITCH ? 0 : as->interp) + (as->interp < 1 && as->prev.anim&ANIM_NOPITCH ? 0 : 1-as->interp); 922 sc.bdata[b.interpindex].mulorient(quat(axis, angle*RAD), b.base); 923 } 924 loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]); 925 } 926 initragdollskelmodel::skeleton927 void initragdoll(ragdolldata &d, skelcacheentry &sc, part *p) 928 { 929 const dualquat *bdata = sc.bdata; 930 loopv(ragdoll->joints) 931 { 932 const ragdollskel::joint &j = ragdoll->joints[i]; 933 const boneinfo &b = bones[j.bone]; 934 const dualquat &q = bdata[b.interpindex]; 935 loopk(3) if(j.vert[k] >= 0) 936 { 937 ragdollskel::vert &v = ragdoll->verts[j.vert[k]]; 938 ragdolldata::vert &dv = d.verts[j.vert[k]]; 939 dv.pos.add(q.transform(v.pos).mul(v.weight)); 940 } 941 } 942 if(ragdoll->animjoints) loopv(ragdoll->joints) 943 { 944 const ragdollskel::joint &j = ragdoll->joints[i]; 945 const boneinfo &b = bones[j.bone]; 946 const dualquat &q = bdata[b.interpindex]; 947 d.calcanimjoint(i, matrix4x3(q)); 948 } 949 loopv(ragdoll->verts) 950 { 951 ragdolldata::vert &dv = d.verts[i]; 952 matrixstack[matrixpos].transform(vec(dv.pos).mul(p->model->scale), dv.pos); 953 } 954 loopv(ragdoll->reljoints) 955 { 956 const ragdollskel::reljoint &r = ragdoll->reljoints[i]; 957 const ragdollskel::joint &j = ragdoll->joints[r.parent]; 958 const boneinfo &br = bones[r.bone], &bj = bones[j.bone]; 959 d.reljoints[i].mul(dualquat(bdata[bj.interpindex]).invert(), bdata[br.interpindex]); 960 } 961 } 962 genragdollbonesskelmodel::skeleton963 void genragdollbones(ragdolldata &d, skelcacheentry &sc, part *p) 964 { 965 if(!sc.bdata) sc.bdata = new dualquat[numinterpbones]; 966 sc.nextversion(); 967 vec trans = vec(d.center).div(p->model->scale).add(p->model->translate); 968 loopv(ragdoll->joints) 969 { 970 const ragdollskel::joint &j = ragdoll->joints[i]; 971 const boneinfo &b = bones[j.bone]; 972 vec pos(0, 0, 0); 973 loopk(3) if(j.vert[k]>=0) pos.add(d.verts[j.vert[k]].pos); 974 pos.mul(j.weight/p->model->scale).sub(trans); 975 matrix4x3 m; 976 m.mul(d.tris[j.tri], pos, d.animjoints ? d.animjoints[i] : j.orient); 977 sc.bdata[b.interpindex] = dualquat(m); 978 } 979 loopv(ragdoll->reljoints) 980 { 981 const ragdollskel::reljoint &r = ragdoll->reljoints[i]; 982 const ragdollskel::joint &j = ragdoll->joints[r.parent]; 983 const boneinfo &br = bones[r.bone], &bj = bones[j.bone]; 984 sc.bdata[br.interpindex].mul(sc.bdata[bj.interpindex], d.reljoints[i]); 985 } 986 loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]); 987 } 988 concattagtransformskelmodel::skeleton989 void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n) 990 { 991 matrix4x3 t; 992 t.mul(bones[tags[i].bone].base, tags[i].matrix); 993 n.mul(m, t); 994 } 995 996 void calctags(part *p, skelcacheentry *sc = NULL) 997 { 998 loopv(p->links) 999 { 1000 linkedpart &l = p->links[i]; 1001 tag &t = tags[l.tag]; 1002 dualquat q; 1003 if(sc) q.mul(sc->bdata[bones[t.bone].interpindex], bones[t.bone].base); 1004 else q = bones[t.bone].base; 1005 matrix4x3 m; 1006 m.mul(q, t.matrix); 1007 m.d.mul(p->model->scale * sizescale); 1008 l.matrix = m; 1009 } 1010 } 1011 1012 void cleanup(bool full = true) 1013 { loopvskelmodel::skeleton1014 loopv(skelcache) 1015 { 1016 skelcacheentry &sc = skelcache[i]; 1017 loopj(MAXANIMPARTS) sc.as[j].cur.fr1 = -1; 1018 DELETEA(sc.bdata); 1019 } 1020 skelcache.setsize(0); 1021 blendoffsets.clear(); 1022 if(full) loopv(users) users[i]->cleanup(); 1023 } 1024 canpreloadskelmodel::skeleton1025 bool canpreload() { return !numframes || gpuaccelerate(); } 1026 preloadskelmodel::skeleton1027 void preload() 1028 { 1029 if(!numframes) return; 1030 if(skelcache.empty()) 1031 { 1032 usegpuskel = gpuaccelerate(); 1033 } 1034 } 1035 checkskelcacheskelmodel::skeleton1036 skelcacheentry &checkskelcache(part *p, const animstate *as, float pitch, const vec &axis, const vec &forward, ragdolldata *rdata) 1037 { 1038 if(skelcache.empty()) 1039 { 1040 usegpuskel = gpuaccelerate(); 1041 } 1042 1043 int numanimparts = ((skelpart *)as->owner)->numanimparts; 1044 uchar *partmask = ((skelpart *)as->owner)->partmask; 1045 skelcacheentry *sc = NULL; 1046 bool match = false; 1047 loopv(skelcache) 1048 { 1049 skelcacheentry &c = skelcache[i]; 1050 loopj(numanimparts) if(c.as[j]!=as[j]) goto mismatch; 1051 if(c.pitch != pitch || c.partmask != partmask || c.ragdoll != rdata || (rdata && c.millis < rdata->lastmove)) goto mismatch; 1052 match = true; 1053 sc = &c; 1054 break; 1055 mismatch: 1056 if(c.millis < lastmillis) { sc = &c; break; } 1057 } 1058 if(!sc) sc = &skelcache.add(); 1059 if(!match) 1060 { 1061 loopi(numanimparts) sc->as[i] = as[i]; 1062 sc->pitch = pitch; 1063 sc->partmask = partmask; 1064 sc->ragdoll = rdata; 1065 if(rdata) genragdollbones(*rdata, *sc, p); 1066 else interpbones(as, pitch, axis, forward, numanimparts, partmask, *sc); 1067 } 1068 sc->millis = lastmillis; 1069 return *sc; 1070 } 1071 getblendoffsetskelmodel::skeleton1072 int getblendoffset(UniformLoc &u) 1073 { 1074 int &offset = blendoffsets.access(Shader::lastshader->program, -1); 1075 if(offset < 0) 1076 { 1077 defformatstring(offsetname, "%s[%d]", u.name, 2*numgpubones); 1078 offset = glGetUniformLocation_(Shader::lastshader->program, offsetname); 1079 } 1080 return offset; 1081 } 1082 setglslbonesskelmodel::skeleton1083 void setglslbones(UniformLoc &u, skelcacheentry &sc, skelcacheentry &bc, int count) 1084 { 1085 if(u.version == bc.version && u.data == bc.bdata) return; 1086 glUniform4fv_(u.loc, 2*numgpubones, sc.bdata[0].real.v); 1087 if(count > 0) 1088 { 1089 int offset = getblendoffset(u); 1090 if(offset >= 0) glUniform4fv_(offset, 2*count, bc.bdata[0].real.v); 1091 } 1092 u.version = bc.version; 1093 u.data = bc.bdata; 1094 } 1095 setgpubonesskelmodel::skeleton1096 void setgpubones(skelcacheentry &sc, blendcacheentry *bc, int count) 1097 { 1098 if(!Shader::lastshader) return; 1099 if(Shader::lastshader->uniformlocs.length() < 1) return; 1100 UniformLoc &u = Shader::lastshader->uniformlocs[0]; 1101 setglslbones(u, sc, bc ? *bc : sc, count); 1102 } 1103 shouldcleanupskelmodel::skeleton1104 bool shouldcleanup() const 1105 { 1106 return numframes && (skelcache.empty() || gpuaccelerate()!=usegpuskel); 1107 } 1108 }; 1109 1110 static hashnameset<skeleton *> skeletons; 1111 1112 struct skelmeshgroup : meshgroup 1113 { 1114 skeleton *skel; 1115 1116 vector<blendcombo> blendcombos; 1117 int numblends[4]; 1118 1119 static const int MAXBLENDCACHE = 16; 1120 blendcacheentry blendcache[MAXBLENDCACHE]; 1121 1122 static const int MAXVBOCACHE = 16; 1123 vbocacheentry vbocache[MAXVBOCACHE]; 1124 1125 ushort *edata; 1126 GLuint ebuf; 1127 int vlen, vertsize, vblends, vweights; 1128 uchar *vdata; 1129 bool usecolor; 1130 1131 skelhitdata *hitdata; 1132 skelmeshgroupskelmodel::skelmeshgroup1133 skelmeshgroup() : skel(NULL), edata(NULL), ebuf(0), vlen(0), vertsize(0), vblends(0), vweights(0), vdata(NULL), usecolor(false), hitdata(NULL) 1134 { 1135 memset(numblends, 0, sizeof(numblends)); 1136 } 1137 ~skelmeshgroupskelmodel::skelmeshgroup1138 virtual ~skelmeshgroup() 1139 { 1140 if(skel) 1141 { 1142 if(skel->shared) skel->users.removeobj(this); 1143 else DELETEP(skel); 1144 } 1145 if(ebuf) glDeleteBuffers_(1, &ebuf); 1146 loopi(MAXBLENDCACHE) 1147 { 1148 DELETEA(blendcache[i].bdata); 1149 } 1150 loopi(MAXVBOCACHE) 1151 { 1152 if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf); 1153 } 1154 DELETEA(vdata); 1155 deletehitdata(); 1156 } 1157 shareskeletonskelmodel::skelmeshgroup1158 void shareskeleton(const char *name) 1159 { 1160 if(!name) 1161 { 1162 skel = new skeleton; 1163 skel->users.add(this); 1164 return; 1165 } 1166 1167 if(skeletons.access(name)) skel = skeletons[name]; 1168 else 1169 { 1170 skel = new skeleton; 1171 skel->name = newstring(name); 1172 skeletons.add(skel); 1173 } 1174 skel->users.add(this); 1175 skel->shared++; 1176 } 1177 findtagskelmodel::skelmeshgroup1178 int findtag(const char *name) 1179 { 1180 return skel->findtag(name); 1181 } 1182 animkeyskelmodel::skelmeshgroup1183 void *animkey() { return skel; } totalframesskelmodel::skelmeshgroup1184 int totalframes() const { return max(skel->numframes, 1); } 1185 loadanimskelmodel::skelmeshgroup1186 virtual skelanimspec *loadanim(const char *filename) { return NULL; } 1187 genvboskelmodel::skelmeshgroup1188 void genvbo(vbocacheentry &vc) 1189 { 1190 if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf); 1191 if(ebuf) return; 1192 1193 vector<ushort> idxs; 1194 1195 vlen = 0; 1196 vblends = 0; 1197 if(skel->numframes && !skel->usegpuskel) 1198 { 1199 vweights = 1; 1200 loopv(blendcombos) 1201 { 1202 blendcombo &c = blendcombos[i]; 1203 c.interpindex = c.weights[1] ? skel->numgpubones + vblends++ : -1; 1204 } 1205 1206 vertsize = sizeof(vvert); 1207 looprendermeshes(skelmesh, m, vlen += m.genvbo(idxs, vlen)); 1208 DELETEA(vdata); 1209 vdata = new uchar[vlen*vertsize]; 1210 looprendermeshes(skelmesh, m, 1211 { 1212 m.fillverts((vvert *)vdata); 1213 }); 1214 } 1215 else 1216 { 1217 if(skel->numframes) 1218 { 1219 vweights = 4; 1220 int availbones = skel->availgpubones() - skel->numgpubones; 1221 while(vweights > 1 && availbones >= numblends[vweights-1]) availbones -= numblends[--vweights]; 1222 loopv(blendcombos) 1223 { 1224 blendcombo &c = blendcombos[i]; 1225 c.interpindex = c.size() > vweights ? skel->numgpubones + vblends++ : -1; 1226 } 1227 } 1228 else 1229 { 1230 vweights = 0; 1231 loopv(blendcombos) blendcombos[i].interpindex = -1; 1232 } 1233 1234 looprendermeshes(skelmesh, m, { if(m.vcolors) { usecolor = true; break; }}); 1235 gle::bindvbo(vc.vbuf); 1236 #define GENVBO(type, args) \ 1237 do \ 1238 { \ 1239 vertsize = sizeof(type); \ 1240 vector<type> vverts; \ 1241 looprendermeshes(skelmesh, m, vlen += m.genvbo args); \ 1242 glBufferData_(GL_ARRAY_BUFFER, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW); \ 1243 } while(0) 1244 #define GENVBOANIM(type) GENVBO(type, (idxs, vlen, vverts)) 1245 #define GENVBOSTAT(type) GENVBO(type, (idxs, vlen, vverts, htdata, htlen)) 1246 if(skel->numframes) 1247 { 1248 if(usecolor) GENVBOANIM(vvertgwc); 1249 else GENVBOANIM(vvertgw); 1250 } 1251 else 1252 { 1253 int numverts = 0, htlen = 128; 1254 looprendermeshes(skelmesh, m, numverts += m.numverts); 1255 while(htlen < numverts) htlen *= 2; 1256 if(numverts*4 > htlen*3) htlen *= 2; 1257 int *htdata = new int[htlen]; 1258 memset(htdata, -1, htlen*sizeof(int)); 1259 if(usecolor) GENVBOSTAT(vvertgc); 1260 else GENVBOSTAT(vvertg); 1261 delete[] htdata; 1262 } 1263 #undef GENVBO 1264 #undef GENVBOANIM 1265 #undef GENVBOSTAT 1266 gle::clearvbo(); 1267 } 1268 1269 glGenBuffers_(1, &ebuf); 1270 gle::bindebo(ebuf); 1271 glBufferData_(GL_ELEMENT_ARRAY_BUFFER, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW); 1272 gle::clearebo(); 1273 } 1274 1275 template<class T> bindbonesskelmodel::skelmeshgroup1276 void bindbones(T *vverts) { if(enablebones) disablebones(); } bindbonesskelmodel::skelmeshgroup1277 void bindbones(vvertgw *vverts) { meshgroup::bindbones(vverts->weights, vverts->bones, vertsize); } bindbonesskelmodel::skelmeshgroup1278 void bindbones(vvertgwc *vverts) { meshgroup::bindbones(vverts->weights, vverts->bones, vertsize); } 1279 1280 template<class T> bindcolorskelmodel::skelmeshgroup1281 void bindcolor(T *vverts) { if(enablecolor) disablecolor(); } bindcolorskelmodel::skelmeshgroup1282 void bindcolor(vvertgc *vverts) { meshgroup::bindcolor(&vverts->col, vertsize); } bindcolorskelmodel::skelmeshgroup1283 void bindcolor(vvertgwc *vverts) { meshgroup::bindcolor(&vverts->col, vertsize); } 1284 1285 template<class T> bindvboskelmodel::skelmeshgroup1286 void bindvbo(const animstate *as, part *p, vbocacheentry &vc) 1287 { 1288 T *vverts = 0; 1289 bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize); 1290 if(as->cur.anim&ANIM_NOSKIN) 1291 { 1292 if(enabletangents) disabletangents(); 1293 1294 if(p->alphatested()) bindtc(&vverts->tc, vertsize); 1295 else if(enabletc) disabletc(); 1296 } 1297 else 1298 { 1299 bindtangents(&vverts->tangent, vertsize); 1300 1301 bindtc(&vverts->tc, vertsize); 1302 } 1303 bindbones(vverts); 1304 bindcolor(vverts); 1305 } 1306 1307 void bindvbo(const animstate *as, part *p, vbocacheentry &vc, skelcacheentry *sc = NULL, blendcacheentry *bc = NULL) 1308 { 1309 if(!skel->numframes) 1310 { 1311 if(usecolor) bindvbo<vvertgc>(as, p, vc); 1312 else bindvbo<vvertg>(as, p, vc); 1313 } 1314 else if(skel->usegpuskel) 1315 { 1316 if(usecolor) bindvbo<vvertgwc>(as, p, vc); 1317 else bindvbo<vvertgw>(as, p, vc); 1318 } 1319 else bindvbo<vvert>(as, p, vc); 1320 } 1321 concattagtransformskelmodel::skelmeshgroup1322 void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n) 1323 { 1324 skel->concattagtransform(p, i, m, n); 1325 } 1326 addblendcomboskelmodel::skelmeshgroup1327 int addblendcombo(const blendcombo &c) 1328 { 1329 loopv(blendcombos) if(blendcombos[i]==c) 1330 { 1331 blendcombos[i].uses += c.uses; 1332 return i; 1333 } 1334 numblends[c.size()-1]++; 1335 blendcombo &a = blendcombos.add(c); 1336 return a.interpindex = blendcombos.length()-1; 1337 } 1338 sortblendcombosskelmodel::skelmeshgroup1339 void sortblendcombos() 1340 { 1341 blendcombos.sort(blendcombo::sortcmp); 1342 int *remap = new int[blendcombos.length()]; 1343 loopv(blendcombos) remap[blendcombos[i].interpindex] = i; 1344 looprendermeshes(skelmesh, m, 1345 { 1346 loopj(m.numverts) 1347 { 1348 vert &v = m.verts[j]; 1349 v.blend = remap[v.blend]; 1350 } 1351 }); 1352 delete[] remap; 1353 } 1354 remapblendskelmodel::skelmeshgroup1355 int remapblend(int blend) 1356 { 1357 const blendcombo &c = blendcombos[blend]; 1358 return c.weights[1] ? c.interpindex : c.interpbones[0]; 1359 } 1360 blendbonesskelmodel::skelmeshgroup1361 static inline void blendbones(dualquat &d, const dualquat *bdata, const blendcombo &c) 1362 { 1363 d = bdata[c.interpbones[0]]; 1364 d.mul(c.weights[0]); 1365 d.accumulate(bdata[c.interpbones[1]], c.weights[1]); 1366 if(c.weights[2]) 1367 { 1368 d.accumulate(bdata[c.interpbones[2]], c.weights[2]); 1369 if(c.weights[3]) d.accumulate(bdata[c.interpbones[3]], c.weights[3]); 1370 } 1371 } 1372 blendbonesskelmodel::skelmeshgroup1373 void blendbones(const skelcacheentry &sc, blendcacheentry &bc) 1374 { 1375 bc.nextversion(); 1376 if(!bc.bdata) bc.bdata = new dualquat[vblends]; 1377 dualquat *dst = bc.bdata - skel->numgpubones; 1378 bool normalize = !skel->usegpuskel || vweights<=1; 1379 loopv(blendcombos) 1380 { 1381 const blendcombo &c = blendcombos[i]; 1382 if(c.interpindex<0) break; 1383 dualquat &d = dst[c.interpindex]; 1384 blendbones(d, sc.bdata, c); 1385 if(normalize) d.normalize(); 1386 } 1387 } 1388 blendbonesskelmodel::skelmeshgroup1389 static inline void blendbones(const dualquat *bdata, dualquat *dst, const blendcombo *c, int numblends) 1390 { 1391 loopi(numblends) 1392 { 1393 dualquat &d = dst[i]; 1394 blendbones(d, bdata, c[i]); 1395 d.normalize(); 1396 } 1397 } 1398 cleanupskelmodel::skelmeshgroup1399 void cleanup() 1400 { 1401 loopi(MAXBLENDCACHE) 1402 { 1403 blendcacheentry &c = blendcache[i]; 1404 DELETEA(c.bdata); 1405 c.owner = -1; 1406 } 1407 loopi(MAXVBOCACHE) 1408 { 1409 vbocacheentry &c = vbocache[i]; 1410 if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; } 1411 c.owner = -1; 1412 } 1413 if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; } 1414 if(skel) skel->cleanup(false); 1415 cleanuphitdata(); 1416 } 1417 1418 #define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck) \ 1419 loopi(cachesize) \ 1420 { \ 1421 cacheentry &c = cache[i]; \ 1422 if(c.owner==owner) \ 1423 { \ 1424 if(c==sc) return c; \ 1425 else c.owner = -1; \ 1426 break; \ 1427 } \ 1428 } \ 1429 loopi(cachesize-1) \ 1430 { \ 1431 cacheentry &c = cache[i]; \ 1432 if(reusecheck c.owner < 0 || c.millis < lastmillis) \ 1433 return c; \ 1434 } \ 1435 return cache[cachesize-1]; 1436 checkvbocacheskelmodel::skelmeshgroup1437 vbocacheentry &checkvbocache(skelcacheentry &sc, int owner) 1438 { 1439 SEARCHCACHE(MAXVBOCACHE, vbocacheentry, vbocache, !c.vbuf || ); 1440 } 1441 checkblendcacheskelmodel::skelmeshgroup1442 blendcacheentry &checkblendcache(skelcacheentry &sc, int owner) 1443 { 1444 SEARCHCACHE(MAXBLENDCACHE, blendcacheentry, blendcache, ) 1445 } 1446 1447 void cleanuphitdata(); 1448 void deletehitdata(); 1449 void buildhitdata(const uchar *hitzones); 1450 void intersect(skelhitdata *z, part *p, const skelmodel::skelcacheentry &sc, const vec &o, const vec &ray); 1451 intersectskelmodel::skelmeshgroup1452 void intersect(const animstate *as, float pitch, const vec &axis, const vec &forward, modelstate *state, dynent *d, part *p, const vec &o, const vec &ray) 1453 { 1454 if(!hitdata) return; 1455 1456 if(skel->shouldcleanup()) skel->cleanup(); 1457 1458 skelcacheentry &sc = skel->checkskelcache(p, as, pitch, axis, forward, !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll || d->ragdoll->millis == lastmillis ? NULL : d->ragdoll); 1459 1460 intersect(hitdata, p, sc, o, ray); 1461 1462 skel->calctags(p, &sc); 1463 } 1464 preloadskelmodel::skelmeshgroup1465 void preload(part *p) 1466 { 1467 if(!skel->canpreload()) return; 1468 if(skel->shouldcleanup()) skel->cleanup(); 1469 skel->preload(); 1470 if(!vbocache->vbuf) genvbo(*vbocache); 1471 } 1472 renderskelmodel::skelmeshgroup1473 void render(const animstate *as, float pitch, const vec &axis, const vec &forward, modelstate *state, dynent *d, part *p) 1474 { 1475 if(skel->shouldcleanup()) { skel->cleanup(); disablevbo(); } 1476 1477 if(!skel->numframes) 1478 { 1479 if(!(as->cur.anim&ANIM_NORENDER)) 1480 { 1481 if(!vbocache->vbuf) genvbo(*vbocache); 1482 bindvbo(as, p, *vbocache); 1483 looprendermeshes(skelmesh, m, 1484 { 1485 p->skins[i].bind(m, as, state); 1486 m.render(as, p->skins[i], *vbocache); 1487 }); 1488 } 1489 skel->calctags(p); 1490 return; 1491 } 1492 1493 skelcacheentry &sc = skel->checkskelcache(p, as, pitch, axis, forward, !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll || d->ragdoll->millis == lastmillis ? NULL : d->ragdoll); 1494 if(!(as->cur.anim&ANIM_NORENDER)) 1495 { 1496 int owner = &sc-&skel->skelcache[0]; 1497 vbocacheentry &vc = skel->usegpuskel ? *vbocache : checkvbocache(sc, owner); 1498 vc.millis = lastmillis; 1499 if(!vc.vbuf) genvbo(vc); 1500 blendcacheentry *bc = NULL; 1501 if(vblends) 1502 { 1503 bc = &checkblendcache(sc, owner); 1504 bc->millis = lastmillis; 1505 if(bc->owner!=owner) 1506 { 1507 bc->owner = owner; 1508 *(animcacheentry *)bc = sc; 1509 blendbones(sc, *bc); 1510 } 1511 } 1512 if(!skel->usegpuskel && vc.owner != owner) 1513 { 1514 vc.owner = owner; 1515 (animcacheentry &)vc = sc; 1516 looprendermeshes(skelmesh, m, 1517 { 1518 m.interpverts(sc.bdata, bc ? bc->bdata : NULL, (vvert *)vdata, p->skins[i]); 1519 }); 1520 gle::bindvbo(vc.vbuf); 1521 glBufferData_(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW); 1522 } 1523 1524 bindvbo(as, p, vc, &sc, bc); 1525 1526 looprendermeshes(skelmesh, m, 1527 { 1528 p->skins[i].bind(m, as, state); 1529 if(skel->usegpuskel) skel->setgpubones(sc, bc, vblends); 1530 m.render(as, p->skins[i], vc); 1531 }); 1532 } 1533 1534 skel->calctags(p, &sc); 1535 1536 if(as->cur.anim&ANIM_RAGDOLL && skel->ragdoll && !d->ragdoll) 1537 { 1538 d->ragdoll = new ragdolldata(skel->ragdoll, p->model->scale); 1539 skel->initragdoll(*d->ragdoll, sc, p); 1540 d->ragdoll->init(d); 1541 } 1542 } 1543 1544 virtual bool load(const char *name, float smooth) = 0; 1545 }; 1546 1547 virtual skelmeshgroup *newmeshes() = 0; 1548 1549 meshgroup *loadmeshes(const char *name, const char *skelname = NULL, float smooth = 2) 1550 { 1551 skelmeshgroup *group = newmeshes(); 1552 group->shareskeleton(skelname); 1553 if(!group->load(name, smooth)) { delete group; return NULL; } 1554 return group; 1555 } 1556 1557 meshgroup *sharemeshes(const char *name, const char *skelname = NULL, float smooth = 2) 1558 { 1559 if(!meshgroups.access(name)) 1560 { 1561 meshgroup *group = loadmeshes(name, skelname, smooth); 1562 if(!group) return NULL; 1563 meshgroups.add(group); 1564 } 1565 return meshgroups[name]; 1566 } 1567 1568 struct animpartmask 1569 { 1570 animpartmask *next; 1571 int numbones; 1572 uchar bones[1]; 1573 }; 1574 1575 struct skelpart : part 1576 { 1577 animpartmask *buildingpartmask; 1578 1579 uchar *partmask; 1580 partskelmodel::skelpart1581 skelpart(animmodel *model, int index = 0) : part(model, index), buildingpartmask(NULL), partmask(NULL) 1582 { 1583 } 1584 ~skelpartskelmodel::skelpart1585 virtual ~skelpart() 1586 { 1587 DELETEA(buildingpartmask); 1588 } 1589 sharepartmaskskelmodel::skelpart1590 uchar *sharepartmask(animpartmask *o) 1591 { 1592 static animpartmask *partmasks = NULL; 1593 animpartmask *p = partmasks; 1594 for(; p; p = p->next) if(p->numbones==o->numbones && !memcmp(p->bones, o->bones, p->numbones)) 1595 { 1596 delete[] (uchar *)o; 1597 return p->bones; 1598 } 1599 1600 o->next = p; 1601 partmasks = o; 1602 return o->bones; 1603 } 1604 newpartmaskskelmodel::skelpart1605 animpartmask *newpartmask() 1606 { 1607 animpartmask *p = (animpartmask *)new uchar[sizeof(animpartmask) + ((skelmeshgroup *)meshes)->skel->numbones-1]; 1608 p->numbones = ((skelmeshgroup *)meshes)->skel->numbones; 1609 memset(p->bones, 0, p->numbones); 1610 return p; 1611 } 1612 initanimpartsskelmodel::skelpart1613 void initanimparts() 1614 { 1615 DELETEA(buildingpartmask); 1616 buildingpartmask = newpartmask(); 1617 } 1618 addanimpartskelmodel::skelpart1619 bool addanimpart(ushort *bonemask) 1620 { 1621 if(!buildingpartmask || numanimparts>=MAXANIMPARTS) return false; 1622 ((skelmeshgroup *)meshes)->skel->applybonemask(bonemask, buildingpartmask->bones, numanimparts); 1623 numanimparts++; 1624 return true; 1625 } 1626 endanimpartsskelmodel::skelpart1627 void endanimparts() 1628 { 1629 if(buildingpartmask) 1630 { 1631 partmask = sharepartmask(buildingpartmask); 1632 buildingpartmask = NULL; 1633 } 1634 1635 ((skelmeshgroup *)meshes)->skel->optimize(); 1636 } 1637 loadedskelmodel::skelpart1638 void loaded() 1639 { 1640 endanimparts(); 1641 part::loaded(); 1642 } 1643 }; 1644 skelmodelskelmodel1645 skelmodel(const char *name) : animmodel(name) 1646 { 1647 } 1648 linktypeskelmodel1649 int linktype(animmodel *m, part *p) const 1650 { 1651 return type()==m->type() && 1652 ((skelmeshgroup *)parts[0]->meshes)->skel == ((skelmeshgroup *)p->meshes)->skel ? 1653 LINK_REUSE : 1654 LINK_TAG; 1655 } 1656 skeletalskelmodel1657 bool skeletal() const { return true; } 1658 addpartskelmodel1659 skelpart &addpart() 1660 { 1661 flushpart(); 1662 skelpart *p = new skelpart(this, parts.length()); 1663 parts.add(p); 1664 return *p; 1665 } 1666 }; 1667 1668 hashnameset<skelmodel::skeleton *> skelmodel::skeletons; 1669 1670 struct skeladjustment 1671 { 1672 float yaw, pitch, roll; 1673 vec translate; 1674 skeladjustmentskeladjustment1675 skeladjustment(float yaw, float pitch, float roll, const vec &translate) : yaw(yaw), pitch(pitch), roll(roll), translate(translate) {} 1676 adjustskeladjustment1677 void adjust(dualquat &dq) 1678 { 1679 if(yaw) dq.mulorient(quat(vec(0, 0, 1), yaw*RAD)); 1680 if(pitch) dq.mulorient(quat(vec(0, -1, 0), pitch*RAD)); 1681 if(roll) dq.mulorient(quat(vec(-1, 0, 0), roll*RAD)); 1682 if(!translate.iszero()) dq.translate(translate); 1683 } 1684 }; 1685 1686 template<class MDL> struct skelloader : modelloader<MDL, skelmodel> 1687 { 1688 static vector<skeladjustment> adjustments; 1689 static vector<uchar> hitzones; 1690 skelloaderskelloader1691 skelloader(const char *name) : modelloader<MDL, skelmodel>(name) {} 1692 flushpartskelloader1693 void flushpart() 1694 { 1695 if(hitzones.length() && skelmodel::parts.length()) 1696 { 1697 skelmodel::skelpart *p = (skelmodel::skelpart *)skelmodel::parts.last(); 1698 skelmodel::skelmeshgroup *m = (skelmodel::skelmeshgroup *)p->meshes; 1699 if(m) m->buildhitdata(hitzones.getbuf()); 1700 } 1701 1702 adjustments.setsize(0); 1703 hitzones.setsize(0); 1704 } 1705 }; 1706 1707 template<class MDL> vector<skeladjustment> skelloader<MDL>::adjustments; 1708 template<class MDL> vector<uchar> skelloader<MDL>::hitzones; 1709 1710 template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmesh> 1711 { 1712 typedef modelcommands<MDL, struct MDL::skelmesh> commands; 1713 typedef struct MDL::skeleton skeleton; 1714 typedef struct MDL::skelmeshgroup meshgroup; 1715 typedef struct MDL::skelpart part; 1716 typedef struct MDL::skin skin; 1717 typedef struct MDL::boneinfo boneinfo; 1718 typedef struct MDL::skelanimspec animspec; 1719 typedef struct MDL::pitchdep pitchdep; 1720 typedef struct MDL::pitchtarget pitchtarget; 1721 typedef struct MDL::pitchcorrect pitchcorrect; 1722 loadpartskelcommands1723 static void loadpart(char *meshfile, char *skelname, float *smooth) 1724 { 1725 if(!MDL::loading) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1726 defformatstring(filename, "%s/%s", MDL::dir, meshfile); 1727 part &mdl = MDL::loading->addpart(); 1728 mdl.meshes = MDL::loading->sharemeshes(path(filename), skelname[0] ? skelname : NULL, *smooth > 0 ? cosf(clamp(*smooth, 0.0f, 180.0f)*RAD) : 2); 1729 if(!mdl.meshes) conoutf("\frCould not load %s", filename); 1730 else 1731 { 1732 if(mdl.meshes && ((meshgroup *)mdl.meshes)->skel->numbones > 0) mdl.disablepitch(); 1733 mdl.initanimparts(); 1734 mdl.initskins(); 1735 } 1736 } 1737 settagskelcommands1738 static void settag(char *name, char *tagname, float *tx, float *ty, float *tz, float *rx, float *ry, float *rz) 1739 { 1740 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1741 part &mdl = *(part *)MDL::loading->parts.last(); 1742 int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1; 1743 if(i >= 0) 1744 { 1745 float cx = *rx ? cosf(*rx/2*RAD) : 1, sx = *rx ? sinf(*rx/2*RAD) : 0, 1746 cy = *ry ? cosf(*ry/2*RAD) : 1, sy = *ry ? sinf(*ry/2*RAD) : 0, 1747 cz = *rz ? cosf(*rz/2*RAD) : 1, sz = *rz ? sinf(*rz/2*RAD) : 0; 1748 matrix4x3 m(matrix3(quat(sx*cy*cz - cx*sy*sz, cx*sy*cz + sx*cy*sz, cx*cy*sz - sx*sy*cz, cx*cy*cz + sx*sy*sz)), 1749 vec(*tx, *ty, *tz)); 1750 ((meshgroup *)mdl.meshes)->skel->addtag(tagname, i, m); 1751 return; 1752 } 1753 conoutf("\frCould not find bone %s for tag %s", name, tagname); 1754 } 1755 setpitchskelcommands1756 static void setpitch(char *name, float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax) 1757 { 1758 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1759 part &mdl = *(part *)MDL::loading->parts.last(); 1760 1761 if(!*name) 1762 { 1763 mdl.pitchscale = *pitchscale; 1764 mdl.pitchoffset = *pitchoffset; 1765 if(*pitchmin || *pitchmax) 1766 { 1767 mdl.pitchmin = *pitchmin; 1768 mdl.pitchmax = *pitchmax; 1769 } 1770 else 1771 { 1772 mdl.pitchmin = -360*fabs(mdl.pitchscale) + mdl.pitchoffset; 1773 mdl.pitchmax = 360*fabs(mdl.pitchscale) + mdl.pitchoffset; 1774 } 1775 return; 1776 } 1777 if(mdl.meshes) 1778 { 1779 vector<int> elems; 1780 if(((meshgroup *)mdl.meshes)->skel->findbones(name, elems)) 1781 { 1782 loopv(elems) 1783 { 1784 boneinfo &b = ((meshgroup *)mdl.meshes)->skel->bones[elems[i]]; 1785 b.pitchscale = *pitchscale; 1786 b.pitchoffset = *pitchoffset; 1787 if(*pitchmin || *pitchmax) 1788 { 1789 b.pitchmin = *pitchmin; 1790 b.pitchmax = *pitchmax; 1791 } 1792 else 1793 { 1794 b.pitchmin = -360*fabs(b.pitchscale) + b.pitchoffset; 1795 b.pitchmax = 360*fabs(b.pitchscale) + b.pitchoffset; 1796 } 1797 } 1798 return; 1799 } 1800 } 1801 conoutf("\frCould not find bone matching %s to pitch", name); 1802 } 1803 setpitchtargetskelcommands1804 static void setpitchtarget(char *name, char *animfile, int *frameoffset, float *pitchmin, float *pitchmax) 1805 { 1806 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1807 part &mdl = *(part *)MDL::loading->parts.last(); 1808 if(!mdl.meshes) return; 1809 defformatstring(filename, "%s/%s", MDL::dir, animfile); 1810 animspec *sa = ((meshgroup *)mdl.meshes)->loadanim(path(filename)); 1811 if(!sa) { conoutf("\frCould not load %s anim file %s", MDL::formatname(), filename); return; } 1812 skeleton *skel = ((meshgroup *)mdl.meshes)->skel; 1813 if(skel) 1814 { 1815 vector<int> elems; 1816 if(skel->findbones(name, elems)) 1817 { 1818 loopv(elems) 1819 { 1820 bool skip = false; 1821 loopvj(skel->pitchtargets) if(skel->pitchtargets[j].bone == elems[i]) { skip = true; break; } 1822 if(skip) continue; 1823 pitchtarget &t = skel->pitchtargets.add(); 1824 t.bone = elems[i]; 1825 t.frame = sa->frame + clamp(*frameoffset, 0, sa->range-1); 1826 t.pitchmin = *pitchmin; 1827 t.pitchmax = *pitchmax; 1828 } 1829 return; 1830 } 1831 } 1832 conoutf("\frCould not find bones matching %s to pitch target", name); 1833 } 1834 setpitchcorrectskelcommands1835 static void setpitchcorrect(char *name, char *targetname, float *scale, float *pitchmin, float *pitchmax) 1836 { 1837 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1838 part &mdl = *(part *)MDL::loading->parts.last(); 1839 if(!mdl.meshes) return; 1840 skeleton *skel = ((meshgroup *)mdl.meshes)->skel; 1841 int bone = skel ? skel->findbone(name) : -1; 1842 if(bone < 0) 1843 { 1844 conoutf("\frCould not find bone %s to pitch correct", name); 1845 return; 1846 } 1847 if(skel->findpitchcorrect(bone) >= 0) return; 1848 int targetbone = skel->findbone(targetname), target = -1; 1849 if(targetbone >= 0) loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == targetbone) { target = i; break; } 1850 if(target < 0) 1851 { 1852 conoutf("\frCould not find pitch target %s to pitch correct %s", targetname, name); 1853 return; 1854 } 1855 pitchcorrect c; 1856 c.bone = bone; 1857 c.target = target; 1858 c.pitchmin = *pitchmin; 1859 c.pitchmax = *pitchmax; 1860 c.pitchscale = *scale; 1861 int pos = skel->pitchcorrects.length(); 1862 loopv(skel->pitchcorrects) if(bone <= skel->pitchcorrects[i].bone) { pos = i; break; } 1863 skel->pitchcorrects.insert(pos, c); 1864 } 1865 setanimskelcommands1866 static void setanim(char *anim, char *animfile, float *speed, int *priority, int *startoffset, int *endoffset) 1867 { 1868 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1869 1870 vector<int> anims; 1871 game::findanims(anim, anims); 1872 if(anims.empty()) conoutf("\frCould not find animation %s", anim); 1873 else 1874 { 1875 part *p = (part *)MDL::loading->parts.last(); 1876 if(!p->meshes) return; 1877 defformatstring(filename, "%s/%s", MDL::dir, animfile); 1878 animspec *sa = ((meshgroup *)p->meshes)->loadanim(path(filename)); 1879 if(!sa) conoutf("\frCould not load %s anim file %s", MDL::formatname(), filename); 1880 else loopv(anims) 1881 { 1882 int start = sa->frame, end = sa->range; 1883 if(*startoffset > 0) start += min(*startoffset, end-1); 1884 else if(*startoffset < 0) start += max(end + *startoffset, 0); 1885 end -= start - sa->frame; 1886 if(*endoffset > 0) end = min(end, *endoffset); 1887 else if(*endoffset < 0) end = max(end + *endoffset, 1); 1888 MDL::loading->parts.last()->setanim(p->numanimparts-1, anims[i], start, end, *speed, *priority); 1889 } 1890 } 1891 } 1892 setanimpartskelcommands1893 static void setanimpart(char *maskstr) 1894 { 1895 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1896 1897 part *p = (part *)MDL::loading->parts.last(); 1898 1899 vector<char *> bonestrs; 1900 explodelist(maskstr, bonestrs); 1901 vector<ushort> bonemask; 1902 loopv(bonestrs) 1903 { 1904 char *bonestr = bonestrs[i]; 1905 int num = 0; 1906 if(p->meshes) 1907 { 1908 vector<int> elems; 1909 if(((meshgroup *)p->meshes)->skel->findbones(bonestr[0]=='!' ? bonestr+1 : bonestr, elems)) loopv(elems) 1910 { 1911 bonemask.add(elems[i] | (bonestr[0]=='!' ? BONEMASK_NOT : 0)); 1912 num++; 1913 } 1914 } 1915 if(!num) conoutf("\frCould not find bone %s for anim part mask [%s]", bonestr, maskstr); 1916 } 1917 bonestrs.deletearrays(); 1918 if(bonemask.empty()) return; 1919 bonemask.sort(); 1920 if(bonemask.length()) bonemask.add(BONEMASK_END); 1921 if(!p->addanimpart(bonemask.getbuf())) conoutf("\frToo many animation parts"); 1922 } 1923 setadjustskelcommands1924 static void setadjust(char *name, float *yaw, float *pitch, float *roll, float *tx, float *ty, float *tz) 1925 { 1926 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1927 part &mdl = *(part *)MDL::loading->parts.last(); 1928 1929 if(!name[0]) return; 1930 if(mdl.meshes) 1931 { 1932 vector<int> elems; 1933 if(((meshgroup *)mdl.meshes)->skel->findbones(name, elems)) 1934 { 1935 loopv(elems) 1936 { 1937 while(!MDL::adjustments.inrange(elems[i])) MDL::adjustments.add(skeladjustment(0, 0, 0, vec(0, 0, 0))); 1938 MDL::adjustments[elems[i]] = skeladjustment(*yaw, *pitch, *roll, vec(*tx/4, *ty/4, *tz/4)); 1939 } 1940 return; 1941 } 1942 } 1943 conoutf("\frCould not find bone %s to adjust", name); 1944 } 1945 sethitzoneskelcommands1946 static void sethitzone(int *id, char *maskstr) 1947 { 1948 if(!MDL::loading || MDL::loading->parts.empty()) { conoutf("\frNot loading an %s", MDL::formatname()); return; } 1949 if(*id >= 0x80) { conoutf("\frInvalid hit zone id %d", *id); return; } 1950 1951 part *p = (part *)MDL::loading->parts.last(); 1952 meshgroup *m = (meshgroup *)p->meshes; 1953 if(!m || m->hitdata) return; 1954 1955 vector<char *> bonestrs; 1956 explodelist(maskstr, bonestrs); 1957 vector<ushort> bonemask; 1958 loopv(bonestrs) 1959 { 1960 char *bonestr = bonestrs[i]; 1961 int num = 0; 1962 if(p->meshes) 1963 { 1964 vector<int> elems; 1965 if(((meshgroup *)p->meshes)->skel->findbones(bonestr[0]=='!' ? bonestr+1 : bonestr, elems)) loopv(elems) 1966 { 1967 bonemask.add(elems[i] | (bonestr[0]=='!' ? BONEMASK_NOT : 0)); 1968 num++; 1969 } 1970 } 1971 if(!num) conoutf("\frCould not find bone %s for hit zone mask [%s]", bonestr, maskstr); 1972 } 1973 bonestrs.deletearrays(); 1974 if(bonemask.empty()) return; 1975 bonemask.sort(); 1976 bonemask.add(BONEMASK_END); 1977 1978 while(MDL::hitzones.length() < m->skel->numbones) MDL::hitzones.add(0xFF); 1979 m->skel->applybonemask(bonemask.getbuf(), MDL::hitzones.getbuf(), *id < 0 ? 0xFF : *id); 1980 } 1981 skelcommandsskelcommands1982 skelcommands() 1983 { 1984 if(MDL::multiparted()) this->modelcommand(loadpart, "load", "ssf"); 1985 this->modelcommand(settag, "tag", "ssffffff"); 1986 this->modelcommand(setpitch, "pitch", "sffff"); 1987 this->modelcommand(setpitchtarget, "pitchtarget", "ssiff"); 1988 this->modelcommand(setpitchcorrect, "pitchcorrect", "ssfff"); 1989 this->modelcommand(sethitzone, "hitzone", "is"); 1990 if(MDL::cananimate()) 1991 { 1992 this->modelcommand(setanim, "anim", "ssfiii"); 1993 this->modelcommand(setanimpart, "animpart", "s"); 1994 this->modelcommand(setadjust, "adjust", "sffffff"); 1995 } 1996 } 1997 }; 1998 1999