Initial Comit: First commit.
[SauerEngine.git] / src / engine / skelmodel.h
blobd778d8166461a46b38a9e1a495fbecafdc5cdc44
1 VARP(gpuskel, 0, 1, 1);
2 VARP(matskel, 0, 1, 1);
4 #define BONEMASK_NOT 0x8000
5 #define BONEMASK_END 0xFFFF
6 #define BONEMASK_BONE 0x7FFF
8 static int bonemaskcmp(ushort *x, ushort *y)
10 if(*x<*y) return -1;
11 if(*x>*y) return 1;
12 return 0;
15 struct skelmodel : animmodel
17 struct vert { vec pos, norm; float u, v; int blend, interpindex; };
18 struct vvert { vec pos; float u, v; };
19 struct vvertn : vvert { vec norm; };
20 struct vvertw : vvertn { uchar weights[4]; uchar bones[4]; };
21 struct vvertbump : vvertn { vec tangent; float bitangent; };
22 struct vvertbumpw : vvertw { vec tangent; float bitangent; };
23 struct bumpvert { vec tangent; float bitangent; };
24 struct tri { ushort vert[3]; };
26 struct blendcombo
28 int uses, interpindex;
29 float weights[4];
30 uchar bones[4], interpbones[4];
32 blendcombo() : uses(1)
36 bool operator==(const blendcombo &c) const
38 loopk(4) if(bones[k] != c.bones[k]) return false;
39 loopk(4) if(weights[k] != c.weights[k]) return false;
40 return true;
43 int size() const
45 int i = 0;
46 while(i < 4 && weights[i]) i++;
47 return i;
50 static int sortcmp(const blendcombo *x, const blendcombo *y)
52 loopi(4)
54 if(x->weights[i])
56 if(!y->weights[i]) return -1;
58 else if(y->weights[i]) return 1;
59 else break;
61 return 0;
64 int addweight(int sorted, float weight, int bone)
66 loopk(sorted) if(weight > weights[k])
68 for(int l = min(sorted-1, 2); l >= k; l--)
70 weights[l+1] = weights[l];
71 bones[l+1] = bones[l];
73 weights[k] = weight;
74 bones[k] = bone;
75 return sorted<4 ? sorted+1 : sorted;
77 if(sorted>=4) return sorted;
78 weights[sorted] = weight;
79 bones[sorted] = bone;
80 return sorted+1;
83 void finalize(int sorted)
85 loopj(4-sorted) { weights[sorted+j] = 0; bones[sorted+j] = 0; }
87 float total = 0;
88 loopj(sorted) total += weights[j];
89 total = 1.0f/total;
90 loopj(sorted) weights[j] *= total;
93 void serialize(vvertw &v)
95 if(interpindex >= 0)
97 v.weights[0] = 255;
98 loopk(3) v.weights[k+1] = 0;
99 v.bones[0] = (matskel ? 3 : 2)*interpindex;
100 loopk(3) v.bones[k+1] = 0;
102 else
104 loopk(4) v.weights[k] = uchar(weights[k]*255);
105 loopk(4) v.bones[k] = (matskel ? 3 : 2)*interpbones[k];
111 struct animcacheentry
113 animstate as[MAXANIMPARTS];
114 float pitch;
115 int millis;
116 uchar *partmask;
118 animcacheentry()
120 loopk(MAXANIMPARTS) as[k].cur.fr1 = as[k].prev.fr1 = -1;
123 bool operator==(const animcacheentry &c) const
125 loopi(MAXANIMPARTS) if(as[i]!=c.as[i]) return false;
126 return pitch==c.pitch && partmask==c.partmask;
130 struct vbocacheentry : animcacheentry
132 uchar *vdata;
133 GLuint vbuf;
134 int owner;
136 vbocacheentry() : vdata(NULL), vbuf(0), owner(-1) {}
139 struct skelcacheentry : animcacheentry
141 dualquat *bdata;
142 matrix3x4 *mdata;
144 skelcacheentry() : bdata(NULL), mdata(NULL) {}
147 struct blendcacheentry : skelcacheentry
149 int owner;
151 blendcacheentry() : owner(-1) {}
154 struct skelmeshgroup;
156 struct skelmesh : mesh
158 vert *verts;
159 bumpvert *bumpverts;
160 tri *tris;
161 int numverts, numtris, maxweights;
163 int voffset, eoffset, elen;
164 ushort minvert, maxvert;
166 skelmesh() : verts(NULL), bumpverts(NULL), tris(0), numverts(0), numtris(0), maxweights(0)
170 virtual ~skelmesh()
172 DELETEA(verts);
173 DELETEA(bumpverts);
174 DELETEA(tris);
177 virtual mesh *allocate() { return new skelmesh; }
179 mesh *copy()
181 skelmesh &m = *(skelmesh *)mesh::copy();
182 m.numverts = numverts;
183 m.verts = new vert[numverts];
184 memcpy(m.verts, verts, numverts*sizeof(vert));
185 m.numtris = numtris;
186 m.tris = new tri[numtris];
187 memcpy(m.tris, tris, numtris*sizeof(tri));
188 m.maxweights = maxweights;
189 if(bumpverts)
191 m.bumpverts = new bumpvert[numverts];
192 memcpy(m.bumpverts, bumpverts, numverts*sizeof(bumpvert));
194 else m.bumpverts = NULL;
195 return &m;
198 int addblendcombo(const blendcombo &c)
200 maxweights = max(maxweights, c.size());
201 return ((skelmeshgroup *)group)->addblendcombo(c);
204 void scaleverts(const vec &transdiff, float scalediff)
206 if(((skelmeshgroup *)group)->skel->numframes) loopi(numverts) verts[i].pos.mul(scalediff);
207 else loopi(numverts) verts[i].pos.add(transdiff).mul(scalediff);
210 void calctangents(bool areaweight = true)
212 if(bumpverts) return;
213 vec *tangent = new vec[2*numverts], *bitangent = tangent+numverts;
214 memset(tangent, 0, 2*numverts*sizeof(vec));
215 bumpverts = new bumpvert[numverts];
216 loopi(numtris)
218 const tri &t = tris[i];
219 const vert &av = verts[t.vert[0]],
220 &bv = verts[t.vert[1]],
221 &cv = verts[t.vert[2]];
223 vec e1(bv.pos), e2(cv.pos);
224 e1.sub(av.pos);
225 e2.sub(av.pos);
227 float u1 = bv.u - av.u, v1 = bv.v - av.v,
228 u2 = cv.u - av.u, v2 = cv.v - av.v,
229 scale = u1*v2 - u2*v1;
230 if(scale!=0) scale = 1.0f / scale;
231 vec u(e1), v(e2);
232 u.mul(v2).sub(vec(e2).mul(v1)).mul(scale);
233 v.mul(u1).sub(vec(e1).mul(u2)).mul(scale);
235 if(!areaweight)
237 u.normalize();
238 v.normalize();
241 loopj(3)
243 tangent[t.vert[j]].add(u);
244 bitangent[t.vert[j]].add(v);
247 loopi(numverts)
249 const vec &n = verts[i].norm,
250 &t = tangent[i],
251 &bt = bitangent[i];
252 bumpvert &bv = bumpverts[i];
253 (bv.tangent = t).sub(vec(n).mul(n.dot(t))).normalize();
254 bv.bitangent = vec().cross(n, t).dot(bt) < 0 ? -1 : 1;
256 delete[] tangent;
259 void calcbb(int frame, vec &bbmin, vec &bbmax, const matrix3x4 &m)
261 loopj(numverts)
263 vec v = m.transform(verts[j].pos);
264 loopi(3)
266 bbmin[i] = min(bbmin[i], v[i]);
267 bbmax[i] = max(bbmax[i], v[i]);
272 void gentris(int frame, Texture *tex, vector<BIH::tri> *out, const matrix3x4 &m)
274 loopj(numtris)
276 BIH::tri &t = out[noclip ? 1 : 0].add();
277 t.tex = tex->bpp==32 ? tex : NULL;
278 vert &av = verts[tris[j].vert[0]],
279 &bv = verts[tris[j].vert[1]],
280 &cv = verts[tris[j].vert[2]];
281 t.a = m.transform(av.pos);
282 t.b = m.transform(bv.pos);
283 t.c = m.transform(cv.pos);
284 t.tc[0] = av.u;
285 t.tc[1] = av.v;
286 t.tc[2] = bv.u;
287 t.tc[3] = bv.v;
288 t.tc[4] = cv.u;
289 t.tc[5] = cv.v;
293 static inline bool comparevert(vvert &w, int j, vert &v)
295 return v.u==w.u && v.v==w.v && v.pos==w.pos;
298 static inline bool comparevert(vvertn &w, int j, vert &v)
300 return v.u==w.u && v.v==w.v && v.pos==w.pos && v.norm==w.norm;
303 inline bool comparevert(vvertbump &w, int j, vert &v)
305 return v.u==w.u && v.v==w.v && v.pos==w.pos && v.norm==w.norm && (!bumpverts || (bumpverts[j].tangent==w.tangent && bumpverts[j].bitangent==w.bitangent));
308 static inline void assignvert(vvert &vv, int j, vert &v, blendcombo &c)
310 vv.pos = v.pos;
311 vv.u = v.u;
312 vv.v = v.v;
315 static inline void assignvert(vvertn &vv, int j, vert &v, blendcombo &c)
317 vv.pos = v.pos;
318 vv.norm = v.norm;
319 vv.u = v.u;
320 vv.v = v.v;
323 inline void assignvert(vvertbump &vv, int j, vert &v, blendcombo &c)
325 vv.pos = v.pos;
326 vv.norm = v.norm;
327 vv.u = v.u;
328 vv.v = v.v;
329 if(bumpverts)
331 vv.tangent = bumpverts[j].tangent;
332 vv.bitangent = bumpverts[j].bitangent;
336 static inline void assignvert(vvertw &vv, int j, vert &v, blendcombo &c)
338 vv.pos = v.pos;
339 vv.norm = v.norm;
340 vv.u = v.u;
341 vv.v = v.v;
342 c.serialize(vv);
345 inline void assignvert(vvertbumpw &vv, int j, vert &v, blendcombo &c)
347 vv.pos = v.pos;
348 vv.norm = v.norm;
349 vv.u = v.u;
350 vv.v = v.v;
351 if(bumpverts)
353 vv.tangent = bumpverts[j].tangent;
354 vv.bitangent = bumpverts[j].bitangent;
356 c.serialize(vv);
359 template<class T>
360 int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts)
362 voffset = offset;
363 eoffset = idxs.length();
364 if(!((skelmeshgroup *)group)->skel->numframes) minvert = 0xFFFF;
365 loopi(numtris)
367 tri &t = tris[i];
368 loopj(3)
370 int index = t.vert[j];
371 vert &v = verts[index];
372 if(!((skelmeshgroup *)group)->skel->numframes)
374 loopvk(vverts)
376 if(comparevert(vverts[k], index, v)) { minvert = min(minvert, (ushort)k); idxs.add((ushort)k); goto found; }
379 idxs.add(vverts.length());
380 assignvert(vverts.add(), index, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
381 found:;
384 elen = idxs.length()-eoffset;
385 if(((skelmeshgroup *)group)->skel->numframes)
387 minvert = voffset;
388 maxvert = voffset + numverts-1;
389 return numverts;
391 else
393 minvert = min(minvert, ushort(voffset));
394 maxvert = max(minvert, ushort(vverts.length()-1));
395 return vverts.length()-voffset;
399 int genvbo(vector<ushort> &idxs, int offset)
401 loopi(numverts) verts[i].interpindex = ((skelmeshgroup *)group)->remapblend(verts[i].blend);
403 voffset = offset;
404 eoffset = idxs.length();
405 loopi(numtris)
407 tri &t = tris[i];
408 loopj(3) idxs.add(voffset+t.vert[j]);
410 minvert = voffset;
411 maxvert = voffset + numverts-1;
412 elen = idxs.length()-eoffset;
413 return numverts;
416 void filltc(uchar *vdata, size_t stride)
418 vdata = (uchar *)&((vvert *)&vdata[voffset*stride])->u;
419 loopi(numverts)
421 ((float *)vdata)[0] = verts[i].u;
422 ((float *)vdata)[1] = verts[i].v;
423 vdata += stride;
427 void fillbump(uchar *vdata, size_t stride)
429 if(stride==sizeof(vvertbumpw)) vdata = (uchar *)&((vvertbumpw *)&vdata[voffset*stride])->tangent;
430 else vdata = (uchar *)&((vvertbump *)&vdata[voffset*stride])->tangent;
431 loopi(numverts)
433 ((bumpvert *)vdata)->bitangent = bumpverts[i].bitangent;
434 vdata += stride;
438 void interpmatverts(skelcacheentry &sc, blendcacheentry *bc, bool norms, bool tangents, void *vdata, skin &s)
440 const int blendoffset = ((skelmeshgroup *)group)->skel->numinterpbones;
441 const matrix3x4 *mdata1 = sc.mdata, *mdata2 = bc ? bc->mdata - blendoffset : NULL;
443 #define IPLOOPMAT(type, dosetup, dotransform) \
444 loopi(numverts) \
446 const vert &src = verts[i]; \
447 type &dst = ((type *)vdata)[i]; \
448 dosetup; \
449 const matrix3x4 &m = (src.interpindex < blendoffset ? mdata1 : mdata2)[src.interpindex]; \
450 dst.pos = m.transform(src.pos); \
451 dotransform; \
454 if(tangents)
456 IPLOOPMAT(vvertbump, bumpvert &bsrc = bumpverts[i],
458 dst.norm = m.transformnormal(src.norm);
459 dst.tangent = m.transformnormal(bsrc.tangent);
462 else if(norms) { IPLOOPMAT(vvertn, , dst.norm = m.transformnormal(src.norm)); }
463 else { IPLOOPMAT(vvert, , ); }
465 #undef IPLOOPMAT
468 void interpverts(skelcacheentry &sc, blendcacheentry *bc, bool norms, bool tangents, void *vdata, skin &s)
470 const int blendoffset = ((skelmeshgroup *)group)->skel->numinterpbones;
471 const dualquat * const bdata1 = sc.bdata, * const bdata2 = bc ? bc->bdata - blendoffset : NULL;
473 #define IPLOOP(type, dosetup, dotransform) \
474 loopi(numverts) \
476 const vert &src = verts[i]; \
477 type &dst = ((type *)vdata)[i]; \
478 dosetup; \
479 const dualquat &d = (src.interpindex < blendoffset ? bdata1 : bdata2)[src.interpindex]; \
480 dst.pos = d.transform(src.pos); \
481 dotransform; \
484 if(tangents)
486 IPLOOP(vvertbump, bumpvert &bsrc = bumpverts[i],
488 dst.norm = d.real.rotate(src.norm);
489 dst.tangent = d.real.rotate(bsrc.tangent);
492 else if(norms) { IPLOOP(vvertn, , dst.norm = d.real.rotate(src.norm)); }
493 else { IPLOOP(vvert, , ); }
495 #undef IPLOOP
498 void setshader(Shader *s)
500 skelmeshgroup *g = (skelmeshgroup *)group;
501 if(glaring)
503 if(!g->skel->usegpuskel) s->variant(0, 2)->set();
504 else if(g->skel->usematskel) s->variant(min(maxweights, g->vweights), 2)->set();
505 else s->variant(maxweights-1, 3)->set();
507 else if(!g->skel->usegpuskel) s->set();
508 else if(g->skel->usematskel) s->variant(min(maxweights, g->vweights)-1, 0)->set();
509 else s->variant(maxweights-1, 1)->set();
512 void render(const animstate *as, skin &s, vbocacheentry &vc)
514 s.bind(this, as);
516 if(!(as->anim&ANIM_NOSKIN))
518 if(s.multitextured())
520 if(!enablemtc || lastmtcbuf!=lastvbuf)
522 glClientActiveTexture_(GL_TEXTURE1_ARB);
523 if(!enablemtc) glEnableClientState(GL_TEXTURE_COORD_ARRAY);
524 if(lastmtcbuf!=lastvbuf)
526 vvert *vverts = hasVBO ? 0 : (vvert *)vc.vdata;
527 glTexCoordPointer(2, GL_FLOAT, ((skelmeshgroup *)group)->vertsize, &vverts->u);
529 glClientActiveTexture_(GL_TEXTURE0_ARB);
530 lastmtcbuf = lastvbuf;
531 enablemtc = true;
534 else if(enablemtc) disablemtc();
536 if(s.tangents())
538 if(!enabletangents || lastnbuf!=lastvbuf)
540 if(!enabletangents) glEnableVertexAttribArray_(1);
541 if(lastnbuf!=lastvbuf)
543 if(((skelmeshgroup *)group)->vertsize==sizeof(vvertbumpw))
545 vvertbumpw *vverts = hasVBO ? 0 : (vvertbumpw *)vc.vdata;
546 glVertexAttribPointer_(1, 4, GL_FLOAT, GL_FALSE, ((skelmeshgroup *)group)->vertsize, &vverts->tangent.x);
548 else
550 vvertbump *vverts = hasVBO ? 0 : (vvertbump *)vc.vdata;
551 glVertexAttribPointer_(1, 4, GL_FLOAT, GL_FALSE, ((skelmeshgroup *)group)->vertsize, &vverts->tangent.x);
554 lastnbuf = lastvbuf;
555 enabletangents = true;
558 else if(enabletangents) disabletangents();
560 if(renderpath==R_FIXEDFUNCTION && (s.scrollu || s.scrollv))
562 glMatrixMode(GL_TEXTURE);
563 glPushMatrix();
564 glTranslatef(s.scrollu*lastmillis/1000.0f, s.scrollv*lastmillis/1000.0f, 0);
566 if(s.multitextured())
568 glActiveTexture_(GL_TEXTURE1_ARB);
569 glPushMatrix();
570 glTranslatef(s.scrollu*lastmillis/1000.0f, s.scrollv*lastmillis/1000.0f, 0);
575 if(hasDRE) glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((skelmeshgroup *)group)->edata[eoffset]);
576 else glDrawElements(GL_TRIANGLES, elen, GL_UNSIGNED_SHORT, &((skelmeshgroup *)group)->edata[eoffset]);
577 glde++;
578 xtravertsva += numverts;
580 if(renderpath==R_FIXEDFUNCTION && !(as->anim&ANIM_NOSKIN) && (s.scrollu || s.scrollv))
582 if(s.multitextured())
584 glPopMatrix();
585 glActiveTexture_(GL_TEXTURE0_ARB);
588 glPopMatrix();
589 glMatrixMode(GL_MODELVIEW);
592 return;
597 struct tag
599 char *name;
600 int bone;
602 tag() : name(NULL) {}
603 ~tag() { DELETEA(name); }
606 struct skelanimspec
608 char *name;
609 int frame, range;
611 skelanimspec() : name(NULL), frame(0), range(0) {}
612 ~skelanimspec()
614 DELETEA(name);
618 struct boneinfo
620 const char *name;
621 int parent, children, next, interpindex, interpparent, interpgroup;
622 float pitchscale, pitchoffset, pitchmin, pitchmax;
623 dualquat base;
625 boneinfo() : name(NULL), parent(-1), children(-1), next(-1), interpindex(-1), interpparent(-1), interpgroup(0), pitchscale(0), pitchoffset(0), pitchmin(0), pitchmax(0) {}
626 ~boneinfo()
628 DELETEA(name);
632 struct skeleton
634 char *name;
635 int shared;
636 vector<skelmeshgroup *> users;
637 boneinfo *bones;
638 int numbones, numinterpbones, numgpubones, numframes, optimizedframes;
639 dualquat *invbones, *framebones;
640 matrix3x4 *matinvbones, *matframebones;
641 vector<skelanimspec> skelanims;
642 vector<tag> tags;
644 bool usegpuskel, usematskel;
645 vector<skelcacheentry> skelcache;
647 skeleton() : name(NULL), shared(0), bones(NULL), numbones(0), numinterpbones(0), numgpubones(0), numframes(0), optimizedframes(0), invbones(NULL), framebones(NULL), matinvbones(NULL), matframebones(NULL), usegpuskel(false), usematskel(false)
651 ~skeleton()
653 DELETEA(name);
654 DELETEA(bones);
655 DELETEA(invbones);
656 DELETEA(framebones);
657 DELETEA(matinvbones);
658 DELETEA(matframebones);
659 loopv(skelcache)
661 DELETEA(skelcache[i].bdata);
662 DELETEA(skelcache[i].mdata);
667 skelanimspec *findskelanim(const char *name)
669 loopv(skelanims)
671 if(skelanims[i].name && !strcmp(name, skelanims[i].name))
672 return &skelanims[i];
674 return NULL;
677 skelanimspec &addskelanim(const char *name)
679 skelanimspec &sa = skelanims.add();
680 sa.name = name ? newstring(name) : NULL;
681 return sa;
684 int findbone(const char *name)
686 loopi(numbones) if(bones[i].name && !strcmp(bones[i].name, name)) return i;
687 return -1;
690 int findtag(const char *name)
692 loopv(tags) if(!strcmp(tags[i].name, name)) return i;
693 return -1;
696 bool addtag(const char *name, int bone)
698 if(findtag(name) >= 0) return false;
699 tag &t = tags.add();
700 t.name = newstring(name);
701 t.bone = bone;
702 return true;
705 skeleton *copy()
707 skeleton &s = *new skeleton;
708 s.numbones = numbones;
709 s.numinterpbones = numinterpbones;
710 s.numgpubones = numgpubones;
711 s.numframes = numframes;
712 s.optimizedframes = optimizedframes;
713 s.bones = new boneinfo[numbones];
714 memcpy(s.bones, bones, numbones*sizeof(boneinfo));
715 loopi(numbones) if(bones[i].name) s.bones[i].name = newstring(bones[i].name);
716 if(numframes)
718 s.framebones = new dualquat[numframes*numbones];
719 memcpy(s.framebones, framebones, numframes*numbones*sizeof(dualquat));
721 loopv(skelanims)
723 skelanimspec &sa = s.addskelanim(skelanims[i].name);
724 sa.frame = skelanims[i].frame;
725 sa.range = skelanims[i].range;
727 loopv(tags)
729 tag &t = s.tags.add();
730 t.name = newstring(tags[i].name);
731 t.bone = tags[i].bone;
733 return &s;
736 void remapbones()
738 loopi(numbones)
740 boneinfo &info = bones[i];
741 info.interpindex = -1;
742 info.interpgroup = i;
744 numgpubones = 0;
745 loopv(users)
747 skelmeshgroup *group = users[i];
748 loopvj(group->blendcombos)
750 blendcombo &c = group->blendcombos[j];
751 int group = c.bones[0];
752 for(int k = 1; k < 4; k++) if(c.weights[k]) group = min(group, int(c.bones[k]));
753 loopk(4) if(c.weights[k])
755 boneinfo &info = bones[c.bones[k]];
756 if(info.interpindex<0) info.interpindex = numgpubones++;
757 c.interpbones[k] = info.interpindex;
758 info.interpgroup = min(info.interpgroup, group);
762 loopi(numbones)
764 int group = bones[i].interpgroup;
765 bones[i].interpgroup = group < i ? bones[group].interpindex : -1;
767 numinterpbones = numgpubones;
768 loopi(numbones)
770 boneinfo &info = bones[i];
771 if(!info.pitchscale) continue;
772 if(info.interpindex < 0) info.interpindex = numinterpbones++;
773 if(info.parent >= 0 && bones[info.parent].interpindex < 0) bones[info.parent].interpindex = numinterpbones++;
775 loopv(tags)
777 boneinfo &info = bones[tags[i].bone];
778 if(info.interpindex < 0) info.interpindex = numinterpbones++;
782 void compactbones()
784 loopi(numbones)
786 boneinfo &info = bones[i];
787 if(info.interpindex < 0) continue;
788 int parent = info.parent;
789 while(parent >= 0 && bones[parent].interpindex < 0) parent = bones[parent].parent;
790 info.interpparent = parent >= 0 ? bones[parent].interpindex : -1;
794 void optimizeframes()
796 while(optimizedframes < numframes)
798 dualquat *frame = &framebones[optimizedframes*numbones];
799 loopi(numbones)
801 boneinfo &info = bones[i];
802 if(info.interpindex < 0 || info.parent < 0 || bones[info.parent].interpindex >= 0)
804 frame[i].fixantipodal(framebones[i]);
805 continue;
807 dualquat d = frame[i];
808 int parent = info.parent;
809 while(parent >= 0 && bones[parent].interpindex < 0)
811 d.mul(frame[parent], dualquat(d));
812 parent = bones[parent].parent;
814 d.normalize();
815 d.fixantipodal(framebones[i]);
816 frame[i] = d;
818 optimizedframes++;
822 void optimize()
824 cleanup();
825 remapbones();
826 compactbones();
827 optimizeframes();
830 void expandbonemask(uchar *expansion, int bone, int val)
832 expansion[bone] = val;
833 bone = bones[bone].children;
834 while(bone>=0) { expandbonemask(expansion, bone, val); bone = bones[bone].next; }
837 void applybonemask(ushort *mask, uchar *partmask, int partindex)
839 if(!mask || *mask==BONEMASK_END) return;
840 uchar *expansion = new uchar[numbones];
841 memset(expansion, *mask&BONEMASK_NOT ? 1 : 0, numbones);
842 while(*mask!=BONEMASK_END)
844 expandbonemask(expansion, *mask&BONEMASK_BONE, *mask&BONEMASK_NOT ? 0 : 1);
845 mask++;
847 loopi(numbones) if(expansion[i]) partmask[i] = partindex;
848 delete[] expansion;
851 void linkchildren()
853 loopi(numbones)
855 boneinfo &b = bones[i];
856 b.children = -1;
857 if(b.parent<0) b.next = -1;
858 else
860 b.next = bones[b.parent].children;
861 bones[b.parent].children = i;
866 int availgpubones() const { return (min(maxvpenvparams - reservevpparams, 256) - 10) / (matskel ? 3 : 2); }
867 bool gpuaccelerate() const { return renderpath!=R_FIXEDFUNCTION && numframes && gpuskel && numgpubones<=availgpubones(); }
869 void scaletags(const vec &transdiff, float scalediff)
871 DELETEA(invbones);
872 DELETEA(matinvbones);
873 DELETEA(matframebones);
874 if(shared > 1) return;
875 loopi(numbones)
877 if(bones[i].parent < 0) bones[i].base.translate(transdiff);
878 bones[i].base.scale(scalediff);
880 loopi(numframes)
882 dualquat *frame = &framebones[i*numbones];
883 loopj(numbones) if(bones[j].interpindex >= 0)
885 if(bones[j].interpparent < 0) frame[j].translate(transdiff);
886 frame[j].scale(scalediff);
891 void geninvbones()
893 if(invbones) return;
894 invbones = new dualquat[numinterpbones];
895 loopi(numbones)
897 boneinfo &info = bones[i];
898 if(info.interpindex < 0) continue;
899 invbones[info.interpindex] = dualquat(info.base).invert();
903 void interpmatbones(const animstate *as, float pitch, const vec &axis, int numanimparts, uchar *partmask, skelcacheentry &sc)
905 if(!invbones) geninvbones();
906 if(!matframebones)
908 matframebones = new matrix3x4[numframes*numbones];
909 loopi(numframes*numbones) matframebones[i] = framebones[i];
911 if(!matinvbones)
913 matinvbones = new matrix3x4[numinterpbones];
914 loopi(numinterpbones) matinvbones[i] = invbones[i];
916 if(!sc.mdata) sc.mdata = new matrix3x4[numinterpbones];
917 struct framedata
919 matrix3x4 *fr1, *fr2, *pfr1, *pfr2;
920 } partframes[MAXANIMPARTS];
921 loopi(numanimparts)
923 partframes[i].fr1 = &matframebones[as[i].cur.fr1*numbones];
924 partframes[i].fr2 = &matframebones[as[i].cur.fr2*numbones];
925 if(as[i].interp<1)
927 partframes[i].pfr1 = &matframebones[as[i].prev.fr1*numbones];
928 partframes[i].pfr2 = &matframebones[as[i].prev.fr2*numbones];
931 loopi(numbones) if(bones[i].interpindex>=0)
933 const animstate &s = as[partmask[i]];
934 const framedata &f = partframes[partmask[i]];
935 matrix3x4 m;
936 (m = f.fr1[i]).scale((1-s.cur.t)*s.interp);
937 m.accumulate(f.fr2[i], s.cur.t*s.interp);
938 if(s.interp<1)
940 m.accumulate(f.pfr1[i], (1-s.prev.t)*(1-s.interp));
941 m.accumulate(f.pfr2[i], s.prev.t*(1-s.interp));
943 const boneinfo &b = bones[i];
944 if(b.pitchscale)
946 float angle = b.pitchscale*pitch + b.pitchoffset;
947 if(b.pitchmin || b.pitchmax) angle = max(b.pitchmin, min(b.pitchmax, angle));
948 matrix3x4 rmat;
949 rmat.rotate(angle*RAD, b.interpparent>=0 ? sc.mdata[b.interpparent].transposedtransformnormal(axis) : axis);
950 m.mul(rmat, matrix3x4(m));
952 if(b.interpparent<0) sc.mdata[b.interpindex] = m;
953 else sc.mdata[b.interpindex].mul(sc.mdata[b.interpparent], m);
955 loopi(numinterpbones)
957 sc.mdata[i].normalize();
958 sc.mdata[i].mul(matinvbones[i]);
962 void interpbones(const animstate *as, float pitch, const vec &axis, int numanimparts, uchar *partmask, skelcacheentry &sc)
964 if(!invbones) geninvbones();
965 if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
966 struct framedata
968 dualquat *fr1, *fr2, *pfr1, *pfr2;
969 } partframes[MAXANIMPARTS];
970 loopi(numanimparts)
972 partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones];
973 partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones];
974 if(as[i].interp<1)
976 partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones];
977 partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones];
980 loopi(numbones) if(bones[i].interpindex>=0)
982 const animstate &s = as[partmask[i]];
983 const framedata &f = partframes[partmask[i]];
984 dualquat d;
985 (d = f.fr1[i]).mul((1-s.cur.t)*s.interp);
986 d.accumulate(f.fr2[i], s.cur.t*s.interp);
987 if(s.interp<1)
989 d.accumulate(f.pfr1[i], (1-s.prev.t)*(1-s.interp));
990 d.accumulate(f.pfr2[i], s.prev.t*(1-s.interp));
992 const boneinfo &b = bones[i];
993 if(b.pitchscale)
995 float angle = b.pitchscale*pitch + b.pitchoffset;
996 if(b.pitchmin || b.pitchmax) angle = max(b.pitchmin, min(b.pitchmax, angle));
997 vec raxis = b.interpparent>=0 ? quat(sc.bdata[b.interpparent].real).invert().rotate(axis) : axis;
998 d.mul(dualquat(quat(raxis, angle*RAD)), dualquat(d));
1000 if(b.interpparent<0) sc.bdata[b.interpindex] = d;
1001 else sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d);
1003 loopi(numinterpbones)
1005 dualquat &d = sc.bdata[i];
1006 d.normalize();
1007 d.mul(invbones[i]);
1009 loopi(numbones)
1011 const boneinfo &b = bones[i];
1012 if(b.interpgroup>=0) sc.bdata[b.interpindex].fixantipodal(sc.bdata[b.interpgroup]);
1016 void concattagtransform(int frame, int i, const matrix3x4 &m, matrix3x4 &n)
1018 matrix3x4 t = bones[tags[i].bone].base;
1019 n.mul(m, t);
1022 void calctagmatrix(int bone, const matrix3x4 &m, linkedpart &l)
1024 matrix3x4 t;
1025 if(numframes) t.mul(m, bones[bone].base);
1026 else t = m;
1027 loopk(4)
1029 l.matrix[4*k] = t.X[k];
1030 l.matrix[4*k+1] = t.Y[k];
1031 l.matrix[4*k+2] = t.Z[k];
1033 l.matrix[3] = l.matrix[7] = l.matrix[11] = 0.0f;
1034 l.matrix[15] = 1.0f;
1037 void calctags(skelcacheentry &sc, part *p)
1039 loopv(p->links)
1041 int tagbone = tags[p->links[i].tag].bone, interpindex = bones[tagbone].interpindex;
1042 calctagmatrix(tagbone, usematskel ? sc.mdata[interpindex] : sc.bdata[interpindex], p->links[i]);
1046 void calctags(part *p)
1048 loopv(p->links)
1050 int tagbone = tags[p->links[i].tag].bone;
1051 calctagmatrix(tagbone, bones[tagbone].base, p->links[i]);
1055 void cleanup()
1057 loopv(skelcache)
1059 skelcacheentry &sc = skelcache[i];
1060 loopj(MAXANIMPARTS) sc.as[j].cur.fr1 = -1;
1061 DELETEA(sc.bdata);
1062 DELETEA(sc.mdata);
1064 skelcache.setsizenodelete(0);
1065 lastsdata = lastbdata = NULL;
1066 loopv(users) users[i]->cleanup();
1069 skelcacheentry &checkskelcache(const animstate *as, float pitch, const vec &axis)
1071 if(skelcache.empty())
1073 usegpuskel = gpuaccelerate();
1074 usematskel = matskel!=0;
1077 int numanimparts = ((skelpart *)as->owner)->numanimparts;
1078 uchar *partmask = ((skelpart *)as->owner)->partmask;
1079 skelcacheentry *sc = NULL;
1080 bool match = false;
1081 loopv(skelcache)
1083 skelcacheentry &c = skelcache[i];
1084 loopj(numanimparts) if(c.as[j]!=as[j]) goto mismatch;
1085 if(c.pitch != pitch || c.partmask != partmask) goto mismatch;
1086 match = true;
1087 sc = &c;
1088 break;
1089 mismatch:
1090 if(c.millis < lastmillis) { sc = &c; break; }
1092 if(!sc) sc = &skelcache.add();
1093 if(!match)
1095 loopi(numanimparts) sc->as[i] = as[i];
1096 sc->pitch = pitch;
1097 sc->partmask = partmask;
1098 if(matskel) interpmatbones(as, pitch, axis, numanimparts, partmask, *sc);
1099 else interpbones(as, pitch, axis, numanimparts, partmask, *sc);
1101 sc->millis = lastmillis;
1102 return *sc;
1105 void setgpubones(skelcacheentry &sc, int count = 0)
1107 if((count ? lastbdata : lastsdata) == (usematskel ? (void *)sc.mdata : (void *)sc.bdata)) return;
1108 int offset = count ? numgpubones : 0;
1109 if(!offset) count = numgpubones;
1110 if(hasPP)
1112 if(usematskel) glProgramEnvParameters4fv_(GL_VERTEX_PROGRAM_ARB, 10 + 3*offset, 3*count, sc.mdata[0].X.v);
1113 else glProgramEnvParameters4fv_(GL_VERTEX_PROGRAM_ARB, 10 + 2*offset, 2*count, sc.bdata[0].real.v);
1115 else if(usematskel) loopi(count)
1117 glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 10 + 3*(offset+i), sc.mdata[i].X.v);
1118 glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 11 + 3*(offset+i), sc.mdata[i].Y.v);
1119 glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 12 + 3*(offset+i), sc.mdata[i].Z.v);
1121 else loopi(count)
1123 glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 10 + 2*(offset+i), sc.bdata[i].real.v);
1124 glProgramEnvParameter4fv_(GL_VERTEX_PROGRAM_ARB, 11 + 2*(offset+i), sc.bdata[i].dual.v);
1126 if(offset) lastbdata = usematskel ? (void *)sc.mdata : (void *)sc.bdata;
1127 else lastsdata = usematskel ? (void *)sc.mdata : (void *)sc.bdata;
1130 bool shouldcleanup() const
1132 return numframes && (skelcache.empty() || gpuaccelerate()!=usegpuskel || (matskel!=0)!=usematskel);
1136 struct skelmeshgroup : meshgroup
1138 skeleton *skel;
1140 vector<blendcombo> blendcombos;
1141 int numblends[4];
1143 static const int MAXBLENDCACHE = 16;
1144 blendcacheentry blendcache[MAXBLENDCACHE];
1146 static const int MAXVBOCACHE = 16;
1147 vbocacheentry vbocache[MAXVBOCACHE];
1149 ushort *edata;
1150 GLuint ebuf;
1151 bool vnorms, vtangents;
1152 int vlen, vertsize, vblends, vweights;
1153 uchar *vdata;
1155 skelmeshgroup() : skel(NULL), edata(NULL), ebuf(0), vdata(NULL)
1157 memset(numblends, 0, sizeof(numblends));
1160 virtual ~skelmeshgroup()
1162 if(skel)
1164 if(skel->shared) skel->users.removeobj(this);
1165 else DELETEP(skel);
1167 if(ebuf) glDeleteBuffers_(1, &ebuf);
1168 loopi(MAXBLENDCACHE)
1170 DELETEA(blendcache[i].bdata);
1171 DELETEA(blendcache[i].mdata);
1173 loopi(MAXVBOCACHE)
1175 DELETEA(vbocache[i].vdata);
1176 if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
1178 DELETEA(vdata);
1181 void shareskeleton(char *name)
1183 if(!name)
1185 skel = new skeleton;
1186 skel->users.add(this);
1187 return;
1190 static hashtable<char *, skeleton *> skeletons;
1191 if(skeletons.access(name)) skel = skeletons[name];
1192 else
1194 skel = new skeleton;
1195 skel->name = newstring(name);
1196 skeletons[skel->name] = skel;
1198 skel->users.add(this);
1199 skel->shared++;
1202 int findtag(const char *name)
1204 return skel->findtag(name);
1207 virtual meshgroup *allocate() { return new skelmeshgroup; }
1209 meshgroup *copy()
1211 skelmeshgroup &group = *(skelmeshgroup *)meshgroup::copy();
1212 group.skel = skel->shared ? skel : skel->copy();
1213 group.skel->users.add(&group);
1214 if(skel->shared) skel->shared++;
1215 loopv(blendcombos) group.blendcombos.add(blendcombos[i]);
1216 memcpy(group.numblends, numblends, sizeof(numblends));
1217 return &group;
1220 int totalframes() const { return max(skel->numframes, 1); }
1222 void scaletags(const vec &transdiff, float scalediff)
1224 skel->scaletags(transdiff, scalediff);
1227 void genvbo(bool norms, bool tangents, vbocacheentry &vc)
1229 if(hasVBO)
1231 if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
1232 if(ebuf) return;
1234 else if(edata)
1236 #define ALLOCVDATA(vdata) \
1237 do \
1239 DELETEA(vdata); \
1240 vdata = new uchar[vlen*vertsize]; \
1241 loopv(meshes) \
1243 skelmesh &m = *(skelmesh *)meshes[i]; \
1244 m.filltc(vdata, vertsize); \
1245 if(tangents) m.fillbump(vdata, vertsize); \
1247 } while(0)
1248 if(!vc.vdata) ALLOCVDATA(vc.vdata);
1249 return;
1252 vector<ushort> idxs;
1254 vnorms = norms;
1255 vtangents = tangents;
1256 vlen = 0;
1257 vblends = 0;
1258 if(skel->numframes && !skel->usegpuskel)
1260 vweights = 1;
1261 loopv(blendcombos)
1263 blendcombo &c = blendcombos[i];
1264 c.interpindex = c.weights[1] ? skel->numinterpbones + vblends++ : -1;
1267 vertsize = tangents ? sizeof(vvertbump) : (norms ? sizeof(vvertn) : sizeof(vvert));
1268 loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo(idxs, vlen);
1269 DELETEA(vdata);
1270 if(hasVBO) ALLOCVDATA(vdata);
1271 else ALLOCVDATA(vc.vdata);
1273 else
1275 if(skel->numframes)
1277 vweights = 4;
1278 int availbones = skel->availgpubones() - skel->numgpubones;
1279 while(vweights > 1 && availbones >= numblends[vweights-1]) availbones -= numblends[--vweights];
1280 loopv(blendcombos)
1282 blendcombo &c = blendcombos[i];
1283 c.interpindex = c.size() > vweights ? skel->numgpubones + vblends++ : -1;
1286 else
1288 vweights = 0;
1289 loopv(blendcombos) blendcombos[i].interpindex = -1;
1292 if(hasVBO) glBindBuffer_(GL_ARRAY_BUFFER_ARB, vc.vbuf);
1293 #define GENVBO(type) \
1294 do \
1296 vertsize = sizeof(type); \
1297 vector<type> vverts; \
1298 loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo(idxs, vlen, vverts); \
1299 if(hasVBO) glBufferData_(GL_ARRAY_BUFFER_ARB, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW_ARB); \
1300 else \
1302 DELETEA(vc.vdata); \
1303 vc.vdata = new uchar[vverts.length()*sizeof(type)]; \
1304 memcpy(vc.vdata, vverts.getbuf(), vverts.length()*sizeof(type)); \
1306 } while(0)
1307 if(skel->numframes)
1309 if(tangents) GENVBO(vvertbumpw);
1310 else GENVBO(vvertw);
1312 else if(tangents) GENVBO(vvertbump);
1313 else if(norms) GENVBO(vvertn);
1314 else GENVBO(vvert);
1317 if(hasVBO)
1319 glGenBuffers_(1, &ebuf);
1320 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, ebuf);
1321 glBufferData_(GL_ELEMENT_ARRAY_BUFFER_ARB, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW_ARB);
1323 else
1325 edata = new ushort[idxs.length()];
1326 memcpy(edata, idxs.getbuf(), idxs.length()*sizeof(ushort));
1328 #undef GENVBO
1329 #undef ALLOCVDATA
1332 void bindvbo(const animstate *as, vbocacheentry &vc, skelcacheentry *sc = NULL, blendcacheentry *bc = NULL)
1334 vvertn *vverts = hasVBO ? 0 : (vvertn *)vc.vdata;
1335 if(hasVBO && lastebuf!=ebuf)
1337 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, ebuf);
1338 lastebuf = ebuf;
1340 if(lastvbuf != (hasVBO ? (void *)(size_t)vc.vbuf : vc.vdata))
1342 if(hasVBO) glBindBuffer_(GL_ARRAY_BUFFER_ARB, vc.vbuf);
1343 if(!lastvbuf) glEnableClientState(GL_VERTEX_ARRAY);
1344 glVertexPointer(3, GL_FLOAT, vertsize, &vverts->pos);
1345 lastvbuf = hasVBO ? (void *)(size_t)vc.vbuf : vc.vdata;
1347 if(as->anim&ANIM_NOSKIN)
1349 if(enabletc) disabletc();
1351 else if(!enabletc || lasttcbuf!=lastvbuf)
1353 if(vnorms || vtangents)
1355 if(!enabletc) glEnableClientState(GL_NORMAL_ARRAY);
1356 if(lasttcbuf!=lastvbuf) glNormalPointer(GL_FLOAT, vertsize, &vverts->norm);
1358 if(!enabletc) glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1359 if(lasttcbuf!=lastvbuf) glTexCoordPointer(2, GL_FLOAT, vertsize, &vverts->u);
1360 lasttcbuf = lastvbuf;
1361 enabletc = true;
1363 if(!sc || !skel->usegpuskel) return;
1364 if(!enablebones)
1366 glEnableVertexAttribArray_(6);
1367 glEnableVertexAttribArray_(7);
1368 enablebones = true;
1370 if(lastbbuf!=lastvbuf)
1372 glVertexAttribPointer_(6, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertsize, &((vvertw *)vverts)->weights);
1373 glVertexAttribPointer_(7, 4, GL_UNSIGNED_BYTE, GL_FALSE, vertsize, &((vvertw *)vverts)->bones);
1374 lastbbuf = lastvbuf;
1376 skel->setgpubones(*sc);
1377 if(bc && vblends) skel->setgpubones(*bc, vblends);
1380 void concattagtransform(int frame, int i, const matrix3x4 &m, matrix3x4 &n)
1382 skel->concattagtransform(frame, i, m, n);
1385 int addblendcombo(const blendcombo &c)
1387 loopv(blendcombos) if(blendcombos[i]==c)
1389 blendcombos[i].uses += c.uses;
1390 return i;
1392 numblends[c.size()-1]++;
1393 blendcombo &a = blendcombos.add(c);
1394 return a.interpindex = blendcombos.length()-1;
1397 void sortblendcombos()
1399 blendcombos.sort(blendcombo::sortcmp);
1400 int *remap = new int[blendcombos.length()];
1401 loopv(blendcombos) remap[blendcombos[i].interpindex] = i;
1402 loopv(meshes)
1404 skelmesh *m = (skelmesh *)meshes[i];
1405 loopj(m->numverts)
1407 vert &v = m->verts[j];
1408 v.blend = remap[v.blend];
1411 delete[] remap;
1414 int remapblend(int blend)
1416 const blendcombo &c = blendcombos[blend];
1417 return c.weights[1] ? c.interpindex : c.interpbones[0];
1420 void blendmatbones(const skelcacheentry &sc, blendcacheentry &bc)
1422 if(!bc.mdata) bc.mdata = new matrix3x4[vblends];
1423 matrix3x4 *dst = bc.mdata - (skel->usegpuskel ? skel->numgpubones : skel->numinterpbones);
1424 loopv(blendcombos)
1426 const blendcombo &c = blendcombos[i];
1427 if(c.interpindex<0) break;
1428 matrix3x4 &m = dst[c.interpindex];
1429 m = sc.mdata[c.interpbones[0]];
1430 m.scale(c.weights[0]);
1431 m.accumulate(sc.mdata[c.interpbones[1]], c.weights[1]);
1432 if(c.weights[2])
1434 m.accumulate(sc.mdata[c.interpbones[2]], c.weights[2]);
1435 if(c.weights[3]) m.accumulate(sc.mdata[c.interpbones[3]], c.weights[3]);
1440 void blendbones(const skelcacheentry &sc, blendcacheentry &bc)
1442 if(!bc.bdata) bc.bdata = new dualquat[vblends];
1443 dualquat *dst = bc.bdata - (skel->usegpuskel ? skel->numgpubones : skel->numinterpbones);
1444 bool normalize = !skel->usegpuskel || vweights<=1;
1445 loopv(blendcombos)
1447 const blendcombo &c = blendcombos[i];
1448 if(c.interpindex<0) break;
1449 dualquat &d = dst[c.interpindex];
1450 d = sc.bdata[c.interpbones[0]];
1451 d.mul(c.weights[0]);
1452 d.accumulate(sc.bdata[c.interpbones[1]], c.weights[1]);
1453 if(c.weights[2])
1455 d.accumulate(sc.bdata[c.interpbones[2]], c.weights[2]);
1456 if(c.weights[3]) d.accumulate(sc.bdata[c.interpbones[3]], c.weights[3]);
1458 if(normalize) d.normalize();
1462 void cleanup()
1464 loopi(MAXBLENDCACHE)
1466 blendcacheentry &c = blendcache[i];
1467 DELETEA(c.bdata);
1468 DELETEA(c.mdata);
1469 c.owner = -1;
1471 loopi(MAXVBOCACHE)
1473 vbocacheentry &c = vbocache[i];
1474 if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
1475 DELETEA(c.vdata);
1476 c.owner = -1;
1478 if(hasVBO) { if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; } }
1479 else DELETEA(vdata);
1480 lastvbuf = lasttcbuf = lastmtcbuf = lastnbuf = lastbbuf = lastbdata = NULL;
1481 lastebuf = 0;
1484 #define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck) \
1485 loopi(cachesize) \
1487 cacheentry &c = cache[i]; \
1488 if(c.owner==owner) \
1490 if(c==sc) return c; \
1491 else c.owner = -1; \
1492 break; \
1495 loopi(cachesize-1) \
1497 cacheentry &c = cache[i]; \
1498 if(reusecheck c.owner < 0 || c.millis < lastmillis) \
1499 return c; \
1501 return cache[cachesize-1];
1503 vbocacheentry &checkvbocache(skelcacheentry &sc, int owner)
1505 SEARCHCACHE(MAXVBOCACHE, vbocacheentry, vbocache, (hasVBO ? !c.vbuf : !c.vdata) || );
1508 blendcacheentry &checkblendcache(skelcacheentry &sc, int owner)
1510 SEARCHCACHE(MAXBLENDCACHE, blendcacheentry, blendcache, )
1513 void render(const animstate *as, float pitch, const vec &axis, part *p)
1515 bool norms = false, tangents = false;
1516 loopv(p->skins)
1518 if(p->skins[i].normals()) norms = true;
1519 if(p->skins[i].tangents()) tangents = true;
1521 if(skel->shouldcleanup()) skel->cleanup();
1522 else if(norms!=vnorms || tangents!=vtangents) cleanup();
1524 if(!skel->numframes)
1526 if(hasVBO ? !vbocache->vbuf : !vbocache->vdata) genvbo(norms, tangents, *vbocache);
1527 bindvbo(as, *vbocache);
1528 loopv(meshes) ((skelmesh *)meshes[i])->render(as, p->skins[i], *vbocache);
1529 skel->calctags(p);
1530 return;
1533 skelcacheentry &sc = skel->checkskelcache(as, pitch, axis);
1534 int owner = &sc-&skel->skelcache[0];
1535 vbocacheentry &vc = skel->usegpuskel ? *vbocache : checkvbocache(sc, owner);
1536 vc.millis = lastmillis;
1537 if(hasVBO ? !vc.vbuf : !vc.vdata) genvbo(norms, tangents, vc);
1538 blendcacheentry *bc = NULL;
1539 if(vblends)
1541 bc = &checkblendcache(sc, owner);
1542 bc->millis = lastmillis;
1543 if(bc->owner!=owner)
1545 bc->owner = owner;
1546 *(animcacheentry *)bc = sc;
1547 if(skel->usematskel) blendmatbones(sc, *bc);
1548 else blendbones(sc, *bc);
1551 if(!skel->usegpuskel && vc.owner!=owner)
1553 vc.owner = owner;
1554 (animcacheentry &)vc = sc;
1555 loopv(meshes)
1557 skelmesh &m = *(skelmesh *)meshes[i];
1558 if(skel->usematskel) m.interpmatverts(sc, bc, norms, tangents, (hasVBO ? vdata : vc.vdata) + m.voffset*vertsize, p->skins[i]);
1559 else m.interpverts(sc, bc, norms, tangents, (hasVBO ? vdata : vc.vdata) + m.voffset*vertsize, p->skins[i]);
1561 if(hasVBO)
1563 glBindBuffer_(GL_ARRAY_BUFFER_ARB, vc.vbuf);
1564 glBufferData_(GL_ARRAY_BUFFER_ARB, vlen*vertsize, vdata, GL_STREAM_DRAW_ARB);
1568 bindvbo(as, vc, &sc, bc);
1569 loopv(meshes) ((skelmesh *)meshes[i])->render(as, p->skins[i], vc);
1571 skel->calctags(sc, p);
1575 struct animpartmask
1577 animpartmask *next;
1578 int numbones;
1579 uchar bones[1];
1582 struct skelpart : part
1584 static animpartmask *buildingpartmask;
1586 uchar *partmask;
1588 skelpart() : partmask(NULL)
1592 virtual ~skelpart()
1594 DELETEA(buildingpartmask);
1597 uchar *sharepartmask(animpartmask *o)
1599 static animpartmask *partmasks = NULL;
1600 animpartmask *p = partmasks;
1601 for(; p; p = p->next) if(p->numbones==o->numbones && !memcmp(p->bones, o->bones, p->numbones))
1603 delete[] (uchar *)o;
1604 return p->bones;
1607 o->next = p;
1608 partmasks = o;
1609 return o->bones;
1612 animpartmask *newpartmask()
1614 animpartmask *p = (animpartmask *)new uchar[sizeof(animpartmask) + ((skelmeshgroup *)meshes)->skel->numbones-1];
1615 p->numbones = ((skelmeshgroup *)meshes)->skel->numbones;
1616 memset(p->bones, 0, p->numbones);
1617 return p;
1620 void initanimparts()
1622 DELETEA(buildingpartmask);
1623 buildingpartmask = newpartmask();
1626 bool addanimpart(ushort *bonemask)
1628 if(!buildingpartmask || numanimparts>=MAXANIMPARTS) return false;
1629 ((skelmeshgroup *)meshes)->skel->applybonemask(bonemask, buildingpartmask->bones, numanimparts);
1630 numanimparts++;
1631 return true;
1634 void endanimparts()
1636 if(buildingpartmask)
1638 partmask = sharepartmask(buildingpartmask);
1639 buildingpartmask = NULL;
1642 ((skelmeshgroup *)meshes)->skel->optimize();
1646 skelmodel(const char *name) : animmodel(name)
1650 int linktype(animmodel *m) const
1652 return type()==m->type() &&
1653 ((skelmeshgroup *)parts[0]->meshes)->skel == ((skelmeshgroup *)m->parts[0]->meshes)->skel ?
1654 LINK_REUSE :
1655 LINK_TAG;
1659 skelmodel::animpartmask *skelmodel::skelpart::buildingpartmask = NULL;