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
)
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]; };
28 int uses
, interpindex
;
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;
46 while(i
< 4 && weights
[i
]) i
++;
50 static int sortcmp(const blendcombo
*x
, const blendcombo
*y
)
56 if(!y
->weights
[i
]) return -1;
58 else if(y
->weights
[i
]) return 1;
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
];
75 return sorted
<4 ? sorted
+1 : sorted
;
77 if(sorted
>=4) return sorted
;
78 weights
[sorted
] = weight
;
83 void finalize(int sorted
)
85 loopj(4-sorted
) { weights
[sorted
+j
] = 0; bones
[sorted
+j
] = 0; }
88 loopj(sorted
) total
+= weights
[j
];
90 loopj(sorted
) weights
[j
] *= total
;
93 void serialize(vvertw
&v
)
98 loopk(3) v
.weights
[k
+1] = 0;
99 v
.bones
[0] = (matskel
? 3 : 2)*interpindex
;
100 loopk(3) v
.bones
[k
+1] = 0;
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
];
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
136 vbocacheentry() : vdata(NULL
), vbuf(0), owner(-1) {}
139 struct skelcacheentry
: animcacheentry
144 skelcacheentry() : bdata(NULL
), mdata(NULL
) {}
147 struct blendcacheentry
: skelcacheentry
151 blendcacheentry() : owner(-1) {}
154 struct skelmeshgroup
;
156 struct skelmesh
: mesh
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)
177 virtual mesh
*allocate() { return new skelmesh
; }
181 skelmesh
&m
= *(skelmesh
*)mesh::copy();
182 m
.numverts
= numverts
;
183 m
.verts
= new vert
[numverts
];
184 memcpy(m
.verts
, verts
, numverts
*sizeof(vert
));
186 m
.tris
= new tri
[numtris
];
187 memcpy(m
.tris
, tris
, numtris
*sizeof(tri
));
188 m
.maxweights
= maxweights
;
191 m
.bumpverts
= new bumpvert
[numverts
];
192 memcpy(m
.bumpverts
, bumpverts
, numverts
*sizeof(bumpvert
));
194 else m
.bumpverts
= NULL
;
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
];
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
);
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
;
232 u
.mul(v2
).sub(vec(e2
).mul(v1
)).mul(scale
);
233 v
.mul(u1
).sub(vec(e1
).mul(u2
)).mul(scale
);
243 tangent
[t
.vert
[j
]].add(u
);
244 bitangent
[t
.vert
[j
]].add(v
);
249 const vec
&n
= verts
[i
].norm
,
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;
259 void calcbb(int frame
, vec
&bbmin
, vec
&bbmax
, const matrix3x4
&m
)
263 vec v
= m
.transform(verts
[j
].pos
);
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
)
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
);
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
)
315 static inline void assignvert(vvertn
&vv
, int j
, vert
&v
, blendcombo
&c
)
323 inline void assignvert(vvertbump
&vv
, int j
, vert
&v
, blendcombo
&c
)
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
)
345 inline void assignvert(vvertbumpw
&vv
, int j
, vert
&v
, blendcombo
&c
)
353 vv
.tangent
= bumpverts
[j
].tangent
;
354 vv
.bitangent
= bumpverts
[j
].bitangent
;
360 int genvbo(vector
<ushort
> &idxs
, int offset
, vector
<T
> &vverts
)
363 eoffset
= idxs
.length();
364 if(!((skelmeshgroup
*)group
)->skel
->numframes
) minvert
= 0xFFFF;
370 int index
= t
.vert
[j
];
371 vert
&v
= verts
[index
];
372 if(!((skelmeshgroup
*)group
)->skel
->numframes
)
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
]);
384 elen
= idxs
.length()-eoffset
;
385 if(((skelmeshgroup
*)group
)->skel
->numframes
)
388 maxvert
= voffset
+ numverts
-1;
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
);
404 eoffset
= idxs
.length();
408 loopj(3) idxs
.add(voffset
+t
.vert
[j
]);
411 maxvert
= voffset
+ numverts
-1;
412 elen
= idxs
.length()-eoffset
;
416 void filltc(uchar
*vdata
, size_t stride
)
418 vdata
= (uchar
*)&((vvert
*)&vdata
[voffset
*stride
])->u
;
421 ((float *)vdata
)[0] = verts
[i
].u
;
422 ((float *)vdata
)[1] = verts
[i
].v
;
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
;
433 ((bumpvert
*)vdata
)->bitangent
= bumpverts
[i
].bitangent
;
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) \
446 const vert &src = verts[i]; \
447 type &dst = ((type *)vdata)[i]; \
449 const matrix3x4 &m = (src.interpindex < blendoffset ? mdata1 : mdata2)[src.interpindex]; \
450 dst.pos = m.transform(src.pos); \
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
, , ); }
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) \
476 const vert &src = verts[i]; \
477 type &dst = ((type *)vdata)[i]; \
479 const dualquat &d = (src.interpindex < blendoffset ? bdata1 : bdata2)[src.interpindex]; \
480 dst.pos = d.transform(src.pos); \
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
, , ); }
498 void setshader(Shader
*s
)
500 skelmeshgroup
*g
= (skelmeshgroup
*)group
;
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
)
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
;
534 else if(enablemtc
) disablemtc();
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
);
550 vvertbump
*vverts
= hasVBO
? 0 : (vvertbump
*)vc
.vdata
;
551 glVertexAttribPointer_(1, 4, GL_FLOAT
, GL_FALSE
, ((skelmeshgroup
*)group
)->vertsize
, &vverts
->tangent
.x
);
555 enabletangents
= true;
558 else if(enabletangents
) disabletangents();
560 if(renderpath
==R_FIXEDFUNCTION
&& (s
.scrollu
|| s
.scrollv
))
562 glMatrixMode(GL_TEXTURE
);
564 glTranslatef(s
.scrollu
*lastmillis
/1000.0f
, s
.scrollv
*lastmillis
/1000.0f
, 0);
566 if(s
.multitextured())
568 glActiveTexture_(GL_TEXTURE1_ARB
);
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
]);
578 xtravertsva
+= numverts
;
580 if(renderpath
==R_FIXEDFUNCTION
&& !(as
->anim
&ANIM_NOSKIN
) && (s
.scrollu
|| s
.scrollv
))
582 if(s
.multitextured())
585 glActiveTexture_(GL_TEXTURE0_ARB
);
589 glMatrixMode(GL_MODELVIEW
);
602 tag() : name(NULL
) {}
603 ~tag() { DELETEA(name
); }
611 skelanimspec() : name(NULL
), frame(0), range(0) {}
621 int parent
, children
, next
, interpindex
, interpparent
, interpgroup
;
622 float pitchscale
, pitchoffset
, pitchmin
, pitchmax
;
625 boneinfo() : name(NULL
), parent(-1), children(-1), next(-1), interpindex(-1), interpparent(-1), interpgroup(0), pitchscale(0), pitchoffset(0), pitchmin(0), pitchmax(0) {}
636 vector
<skelmeshgroup
*> users
;
638 int numbones
, numinterpbones
, numgpubones
, numframes
, optimizedframes
;
639 dualquat
*invbones
, *framebones
;
640 matrix3x4
*matinvbones
, *matframebones
;
641 vector
<skelanimspec
> skelanims
;
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)
657 DELETEA(matinvbones
);
658 DELETEA(matframebones
);
661 DELETEA(skelcache
[i
].bdata
);
662 DELETEA(skelcache
[i
].mdata
);
667 skelanimspec
*findskelanim(const char *name
)
671 if(skelanims
[i
].name
&& !strcmp(name
, skelanims
[i
].name
))
672 return &skelanims
[i
];
677 skelanimspec
&addskelanim(const char *name
)
679 skelanimspec
&sa
= skelanims
.add();
680 sa
.name
= name
? newstring(name
) : NULL
;
684 int findbone(const char *name
)
686 loopi(numbones
) if(bones
[i
].name
&& !strcmp(bones
[i
].name
, name
)) return i
;
690 int findtag(const char *name
)
692 loopv(tags
) if(!strcmp(tags
[i
].name
, name
)) return i
;
696 bool addtag(const char *name
, int bone
)
698 if(findtag(name
) >= 0) return false;
700 t
.name
= newstring(name
);
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
);
718 s
.framebones
= new dualquat
[numframes
*numbones
];
719 memcpy(s
.framebones
, framebones
, numframes
*numbones
*sizeof(dualquat
));
723 skelanimspec
&sa
= s
.addskelanim(skelanims
[i
].name
);
724 sa
.frame
= skelanims
[i
].frame
;
725 sa
.range
= skelanims
[i
].range
;
729 tag
&t
= s
.tags
.add();
730 t
.name
= newstring(tags
[i
].name
);
731 t
.bone
= tags
[i
].bone
;
740 boneinfo
&info
= bones
[i
];
741 info
.interpindex
= -1;
742 info
.interpgroup
= i
;
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
);
764 int group
= bones
[i
].interpgroup
;
765 bones
[i
].interpgroup
= group
< i
? bones
[group
].interpindex
: -1;
767 numinterpbones
= numgpubones
;
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
++;
777 boneinfo
&info
= bones
[tags
[i
].bone
];
778 if(info
.interpindex
< 0) info
.interpindex
= numinterpbones
++;
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
];
801 boneinfo
&info
= bones
[i
];
802 if(info
.interpindex
< 0 || info
.parent
< 0 || bones
[info
.parent
].interpindex
>= 0)
804 frame
[i
].fixantipodal(framebones
[i
]);
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
;
815 d
.fixantipodal(framebones
[i
]);
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);
847 loopi(numbones
) if(expansion
[i
]) partmask
[i
] = partindex
;
855 boneinfo
&b
= bones
[i
];
857 if(b
.parent
<0) b
.next
= -1;
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
)
872 DELETEA(matinvbones
);
873 DELETEA(matframebones
);
874 if(shared
> 1) return;
877 if(bones
[i
].parent
< 0) bones
[i
].base
.translate(transdiff
);
878 bones
[i
].base
.scale(scalediff
);
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
);
894 invbones
= new dualquat
[numinterpbones
];
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();
908 matframebones
= new matrix3x4
[numframes
*numbones
];
909 loopi(numframes
*numbones
) matframebones
[i
] = framebones
[i
];
913 matinvbones
= new matrix3x4
[numinterpbones
];
914 loopi(numinterpbones
) matinvbones
[i
] = invbones
[i
];
916 if(!sc
.mdata
) sc
.mdata
= new matrix3x4
[numinterpbones
];
919 matrix3x4
*fr1
, *fr2
, *pfr1
, *pfr2
;
920 } partframes
[MAXANIMPARTS
];
923 partframes
[i
].fr1
= &matframebones
[as
[i
].cur
.fr1
*numbones
];
924 partframes
[i
].fr2
= &matframebones
[as
[i
].cur
.fr2
*numbones
];
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
]];
936 (m
= f
.fr1
[i
]).scale((1-s
.cur
.t
)*s
.interp
);
937 m
.accumulate(f
.fr2
[i
], s
.cur
.t
*s
.interp
);
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
];
946 float angle
= b
.pitchscale
*pitch
+ b
.pitchoffset
;
947 if(b
.pitchmin
|| b
.pitchmax
) angle
= max(b
.pitchmin
, min(b
.pitchmax
, angle
));
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
];
968 dualquat
*fr1
, *fr2
, *pfr1
, *pfr2
;
969 } partframes
[MAXANIMPARTS
];
972 partframes
[i
].fr1
= &framebones
[as
[i
].cur
.fr1
*numbones
];
973 partframes
[i
].fr2
= &framebones
[as
[i
].cur
.fr2
*numbones
];
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
]];
985 (d
= f
.fr1
[i
]).mul((1-s
.cur
.t
)*s
.interp
);
986 d
.accumulate(f
.fr2
[i
], s
.cur
.t
*s
.interp
);
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
];
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
];
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
;
1022 void calctagmatrix(int bone
, const matrix3x4
&m
, linkedpart
&l
)
1025 if(numframes
) t
.mul(m
, bones
[bone
].base
);
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
)
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
)
1050 int tagbone
= tags
[p
->links
[i
].tag
].bone
;
1051 calctagmatrix(tagbone
, bones
[tagbone
].base
, p
->links
[i
]);
1059 skelcacheentry
&sc
= skelcache
[i
];
1060 loopj(MAXANIMPARTS
) sc
.as
[j
].cur
.fr1
= -1;
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
;
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
;
1090 if(c
.millis
< lastmillis
) { sc
= &c
; break; }
1092 if(!sc
) sc
= &skelcache
.add();
1095 loopi(numanimparts
) sc
->as
[i
] = as
[i
];
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
;
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
;
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
);
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
1140 vector
<blendcombo
> blendcombos
;
1143 static const int MAXBLENDCACHE
= 16;
1144 blendcacheentry blendcache
[MAXBLENDCACHE
];
1146 static const int MAXVBOCACHE
= 16;
1147 vbocacheentry vbocache
[MAXVBOCACHE
];
1151 bool vnorms
, vtangents
;
1152 int vlen
, vertsize
, vblends
, vweights
;
1155 skelmeshgroup() : skel(NULL
), edata(NULL
), ebuf(0), vdata(NULL
)
1157 memset(numblends
, 0, sizeof(numblends
));
1160 virtual ~skelmeshgroup()
1164 if(skel
->shared
) skel
->users
.removeobj(this);
1167 if(ebuf
) glDeleteBuffers_(1, &ebuf
);
1168 loopi(MAXBLENDCACHE
)
1170 DELETEA(blendcache
[i
].bdata
);
1171 DELETEA(blendcache
[i
].mdata
);
1175 DELETEA(vbocache
[i
].vdata
);
1176 if(vbocache
[i
].vbuf
) glDeleteBuffers_(1, &vbocache
[i
].vbuf
);
1181 void shareskeleton(char *name
)
1185 skel
= new skeleton
;
1186 skel
->users
.add(this);
1190 static hashtable
<char *, skeleton
*> skeletons
;
1191 if(skeletons
.access(name
)) skel
= skeletons
[name
];
1194 skel
= new skeleton
;
1195 skel
->name
= newstring(name
);
1196 skeletons
[skel
->name
] = skel
;
1198 skel
->users
.add(this);
1202 int findtag(const char *name
)
1204 return skel
->findtag(name
);
1207 virtual meshgroup
*allocate() { return new skelmeshgroup
; }
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
));
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
)
1231 if(!vc
.vbuf
) glGenBuffers_(1, &vc
.vbuf
);
1236 #define ALLOCVDATA(vdata) \
1240 vdata = new uchar[vlen*vertsize]; \
1243 skelmesh &m = *(skelmesh *)meshes[i]; \
1244 m.filltc(vdata, vertsize); \
1245 if(tangents) m.fillbump(vdata, vertsize); \
1248 if(!vc
.vdata
) ALLOCVDATA(vc
.vdata
);
1252 vector
<ushort
> idxs
;
1255 vtangents
= tangents
;
1258 if(skel
->numframes
&& !skel
->usegpuskel
)
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
);
1270 if(hasVBO
) ALLOCVDATA(vdata
);
1271 else ALLOCVDATA(vc
.vdata
);
1278 int availbones
= skel
->availgpubones() - skel
->numgpubones
;
1279 while(vweights
> 1 && availbones
>= numblends
[vweights
-1]) availbones
-= numblends
[--vweights
];
1282 blendcombo
&c
= blendcombos
[i
];
1283 c
.interpindex
= c
.size() > vweights
? skel
->numgpubones
+ vblends
++ : -1;
1289 loopv(blendcombos
) blendcombos
[i
].interpindex
= -1;
1292 if(hasVBO
) glBindBuffer_(GL_ARRAY_BUFFER_ARB
, vc
.vbuf
);
1293 #define GENVBO(type) \
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); \
1302 DELETEA(vc.vdata); \
1303 vc.vdata = new uchar[vverts.length()*sizeof(type)]; \
1304 memcpy(vc.vdata, vverts.getbuf(), vverts.length()*sizeof(type)); \
1309 if(tangents
) GENVBO(vvertbumpw
);
1310 else GENVBO(vvertw
);
1312 else if(tangents
) GENVBO(vvertbump
);
1313 else if(norms
) GENVBO(vvertn
);
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
);
1325 edata
= new ushort
[idxs
.length()];
1326 memcpy(edata
, idxs
.getbuf(), idxs
.length()*sizeof(ushort
));
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
);
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
;
1363 if(!sc
|| !skel
->usegpuskel
) return;
1366 glEnableVertexAttribArray_(6);
1367 glEnableVertexAttribArray_(7);
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
;
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
;
1404 skelmesh
*m
= (skelmesh
*)meshes
[i
];
1407 vert
&v
= m
->verts
[j
];
1408 v
.blend
= remap
[v
.blend
];
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
);
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]);
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;
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]);
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();
1464 loopi(MAXBLENDCACHE
)
1466 blendcacheentry
&c
= blendcache
[i
];
1473 vbocacheentry
&c
= vbocache
[i
];
1474 if(c
.vbuf
) { glDeleteBuffers_(1, &c
.vbuf
); c
.vbuf
= 0; }
1478 if(hasVBO
) { if(ebuf
) { glDeleteBuffers_(1, &ebuf
); ebuf
= 0; } }
1479 else DELETEA(vdata
);
1480 lastvbuf
= lasttcbuf
= lastmtcbuf
= lastnbuf
= lastbbuf
= lastbdata
= NULL
;
1484 #define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck) \
1487 cacheentry &c = cache[i]; \
1488 if(c.owner==owner) \
1490 if(c==sc) return c; \
1491 else c.owner = -1; \
1495 loopi(cachesize-1) \
1497 cacheentry &c = cache[i]; \
1498 if(reusecheck c.owner < 0 || c.millis < lastmillis) \
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;
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
);
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
;
1541 bc
= &checkblendcache(sc
, owner
);
1542 bc
->millis
= lastmillis
;
1543 if(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
)
1554 (animcacheentry
&)vc
= sc
;
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
]);
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
);
1582 struct skelpart
: part
1584 static animpartmask
*buildingpartmask
;
1588 skelpart() : partmask(NULL
)
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
;
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
);
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
);
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
?
1659 skelmodel::animpartmask
*skelmodel::skelpart::buildingpartmask
= NULL
;