1 struct vertmodel
: animmodel
3 struct vert
{ vec norm
, pos
; };
4 struct vvertff
{ vec pos
; float u
, v
; };
5 struct vvert
: vvertff
{ vec norm
; };
6 struct vvertbump
: vvert
{ vec tangent
; float bitangent
; };
7 struct tcvert
{ float u
, v
; };
8 struct bumpvert
{ vec tangent
; float bitangent
; };
9 struct tri
{ ushort vert
[3]; };
18 vbocacheentry() : vdata(NULL
), vbuf(0) { as
.cur
.fr1
= as
.prev
.fr1
= -1; }
21 struct vertmesh
: mesh
27 int numverts
, numtris
;
29 int voffset
, eoffset
, elen
;
30 ushort minvert
, maxvert
;
32 vertmesh() : verts(0), tcverts(0), bumpverts(0), tris(0)
44 virtual mesh
*allocate() { return new vertmesh
; }
48 vertmesh
&m
= *(vertmesh
*)mesh::copy();
49 m
.numverts
= numverts
;
50 m
.verts
= new vert
[numverts
*((vertmeshgroup
*)group
)->numframes
];
51 memcpy(m
.verts
, verts
, numverts
*((vertmeshgroup
*)group
)->numframes
*sizeof(vert
));
52 m
.tcverts
= new tcvert
[numverts
];
53 memcpy(m
.tcverts
, tcverts
, numverts
*sizeof(tcvert
));
55 m
.tris
= new tri
[numtris
];
56 memcpy(m
.tris
, tris
, numtris
*sizeof(tri
));
59 m
.bumpverts
= new bumpvert
[numverts
];
60 memcpy(m
.bumpverts
, bumpverts
, numverts
*sizeof(bumpvert
));
62 else m
.bumpverts
= NULL
;
66 void scaleverts(const vec
&transdiff
, float scalediff
)
68 loopi(((vertmeshgroup
*)group
)->numframes
*numverts
) verts
[i
].pos
.add(transdiff
).mul(scalediff
);
74 vec
*tangent
= new vec
[2*numverts
], *bitangent
= tangent
+numverts
;
75 memset(tangent
, 0, 2*numverts
*sizeof(vec
));
76 bumpverts
= new bumpvert
[((vertmeshgroup
*)group
)->numframes
*numverts
];
77 loopk(((vertmeshgroup
*)group
)->numframes
)
79 vert
*fverts
= &verts
[k
*numverts
];
82 const tri
&t
= tris
[i
];
83 const tcvert
&tc0
= tcverts
[t
.vert
[0]],
84 &tc1
= tcverts
[t
.vert
[1]],
85 &tc2
= tcverts
[t
.vert
[2]];
87 vec
v0(fverts
[t
.vert
[0]].pos
),
88 e1(fverts
[t
.vert
[1]].pos
),
89 e2(fverts
[t
.vert
[2]].pos
);
93 float u1
= tc1
.u
- tc0
.u
, v1
= tc1
.v
- tc0
.v
,
94 u2
= tc2
.u
- tc0
.u
, v2
= tc2
.v
- tc0
.v
,
95 scale
= u1
*v2
- u2
*v1
;
96 if(scale
!=0) scale
= 1.0f
/ scale
;
98 u
.mul(v2
).sub(vec(e2
).mul(v1
)).mul(scale
);
99 v
.mul(u1
).sub(vec(e1
).mul(u2
)).mul(scale
);
103 tangent
[t
.vert
[j
]].add(u
);
104 bitangent
[t
.vert
[j
]].add(v
);
107 bumpvert
*fbumpverts
= &bumpverts
[k
*numverts
];
110 const vec
&n
= fverts
[i
].norm
,
113 bumpvert
&bv
= fbumpverts
[i
];
114 (bv
.tangent
= t
).sub(vec(n
).mul(n
.dot(t
))).normalize();
115 bv
.bitangent
= vec().cross(n
, t
).dot(bt
) < 0 ? -1 : 1;
121 void calcbb(int frame
, vec
&bbmin
, vec
&bbmax
, const matrix3x4
&m
)
123 vert
*fverts
= &verts
[frame
*numverts
];
126 vec v
= m
.transform(fverts
[j
].pos
);
129 bbmin
[i
] = min(bbmin
[i
], v
[i
]);
130 bbmax
[i
] = max(bbmax
[i
], v
[i
]);
135 void gentris(int frame
, Texture
*tex
, vector
<BIH::tri
> *out
, const matrix3x4
&m
)
137 vert
*fverts
= &verts
[frame
*numverts
];
140 BIH::tri
&t
= out
[noclip
? 1 : 0].add();
141 t
.tex
= tex
->bpp
==32 ? tex
: NULL
;
142 t
.a
= m
.transform(fverts
[tris
[j
].vert
[0]].pos
);
143 t
.b
= m
.transform(fverts
[tris
[j
].vert
[1]].pos
);
144 t
.c
= m
.transform(fverts
[tris
[j
].vert
[2]].pos
);
145 tcvert
&av
= tcverts
[tris
[j
].vert
[0]],
146 &bv
= tcverts
[tris
[j
].vert
[1]],
147 &cv
= tcverts
[tris
[j
].vert
[2]];
157 static inline bool comparevert(vvertff
&w
, int j
, tcvert
&tc
, vert
&v
)
159 return tc
.u
==w
.u
&& tc
.v
==w
.v
&& v
.pos
==w
.pos
;
162 static inline bool comparevert(vvert
&w
, int j
, tcvert
&tc
, vert
&v
)
164 return tc
.u
==w
.u
&& tc
.v
==w
.v
&& v
.pos
==w
.pos
&& v
.norm
==w
.norm
;
167 inline bool comparevert(vvertbump
&w
, int j
, tcvert
&tc
, vert
&v
)
169 return tc
.u
==w
.u
&& tc
.v
==w
.v
&& v
.pos
==w
.pos
&& v
.norm
==w
.norm
&& (!bumpverts
|| (bumpverts
[j
].tangent
==w
.tangent
&& bumpverts
[j
].bitangent
==w
.bitangent
));
172 static inline void assignvert(vvertff
&vv
, int j
, tcvert
&tc
, vert
&v
)
179 static inline void assignvert(vvert
&vv
, int j
, tcvert
&tc
, vert
&v
)
187 inline void assignvert(vvertbump
&vv
, int j
, tcvert
&tc
, vert
&v
)
195 vv
.tangent
= bumpverts
[j
].tangent
;
196 vv
.bitangent
= bumpverts
[j
].bitangent
;
201 int genvbo(vector
<ushort
> &idxs
, int offset
, vector
<T
> &vverts
)
204 eoffset
= idxs
.length();
211 int index
= t
.vert
[j
];
212 tcvert
&tc
= tcverts
[index
];
213 vert
&v
= verts
[index
];
216 if(comparevert(vverts
[k
], index
, tc
, v
)) { minvert
= min(minvert
, (ushort
)k
); idxs
.add((ushort
)k
); goto found
; }
218 idxs
.add(vverts
.length());
219 assignvert(vverts
.add(), index
, tc
, v
);
223 minvert
= min(minvert
, ushort(voffset
));
224 maxvert
= max(minvert
, ushort(vverts
.length()-1));
225 elen
= idxs
.length()-eoffset
;
226 return vverts
.length()-voffset
;
229 int genvbo(vector
<ushort
> &idxs
, int offset
)
232 eoffset
= idxs
.length();
236 loopj(3) idxs
.add(voffset
+t
.vert
[j
]);
239 maxvert
= voffset
+ numverts
-1;
240 elen
= idxs
.length()-eoffset
;
244 void filltc(uchar
*vdata
, size_t stride
)
246 vdata
= (uchar
*)&((vvertff
*)&vdata
[voffset
*stride
])->u
;
249 *(tcvert
*)vdata
= tcverts
[i
];
254 void interpverts(const animstate
&as
, bool norms
, bool tangents
, void *vdata
, skin
&s
)
256 vert
*vert1
= &verts
[as
.cur
.fr1
* numverts
],
257 *vert2
= &verts
[as
.cur
.fr2
* numverts
],
258 *pvert1
= as
.interp
<1 ? &verts
[as
.prev
.fr1
* numverts
] : NULL
,
259 *pvert2
= as
.interp
<1 ? &verts
[as
.prev
.fr2
* numverts
] : NULL
;
260 #define ip(p1, p2, t) (p1+t*(p2-p1))
261 #define ip_v(p, c, t) ip(p##1[i].c, p##2[i].c, t)
262 #define ip_v_ai(c) ip(ip_v(pvert, c, as.prev.t), ip_v(vert, c, as.cur.t), as.interp)
263 #define ip_pos vec(ip_v(vert, pos.x, as.cur.t), ip_v(vert, pos.y, as.cur.t), ip_v(vert, pos.z, as.cur.t))
264 #define ip_pos_ai vec(ip_v_ai(pos.x), ip_v_ai(pos.y), ip_v_ai(pos.z))
265 #define ip_norm vec(ip_v(vert, norm.x, as.cur.t), ip_v(vert, norm.y, as.cur.t), ip_v(vert, norm.z, as.cur.t))
266 #define ip_norm_ai vec(ip_v_ai(norm.x), ip_v_ai(norm.y), ip_v_ai(norm.z))
267 #define ip_b_ai(c) ip(ip_v(bpvert, c, as.prev.t), ip_v(bvert, c, as.cur.t), as.interp)
268 #define ip_tangent vec(ip_v(bvert, tangent.x, as.cur.t), ip_v(bvert, tangent.y, as.cur.t), ip_v(bvert, tangent.z, as.cur.t))
269 #define ip_tangent_ai vec(ip_b_ai(tangent.x), ip_b_ai(tangent.y), ip_b_ai(tangent.z))
270 #define iploop(type, body) \
273 type &v = ((type *)vdata)[i]; \
278 bumpvert
*bvert1
= &bumpverts
[as
.cur
.fr1
* numverts
],
279 *bvert2
= &bumpverts
[as
.cur
.fr2
* numverts
],
280 *bpvert1
= as
.interp
<1 ? &bumpverts
[as
.prev
.fr1
* numverts
] : NULL
,
281 *bpvert2
= as
.interp
<1 ? &bumpverts
[as
.prev
.fr2
* numverts
] : NULL
;
282 if(as
.interp
<1) iploop(vvertbump
, { v
.pos
= ip_pos_ai
; v
.norm
= ip_norm_ai
; v
.tangent
= ip_tangent_ai
; v
.bitangent
= bvert1
[i
].bitangent
; })
283 else iploop(vvertbump
, { v
.pos
= ip_pos
; v
.norm
= ip_norm
; v
.tangent
= ip_tangent
; v
.bitangent
= bvert1
[i
].bitangent
; })
287 if(as
.interp
<1) iploop(vvert
, { v
.pos
= ip_pos_ai
; v
.norm
= ip_norm_ai
; })
288 else iploop(vvert
, { v
.pos
= ip_pos
; v
.norm
= ip_norm
; })
290 else if(as
.interp
<1) iploop(vvertff
, v
.pos
= ip_pos_ai
)
291 else iploop(vvertff
, v
.pos
= ip_pos
)
305 void render(const animstate
*as
, skin
&s
, vbocacheentry
&vc
)
309 if(!(as
->anim
&ANIM_NOSKIN
))
311 if(s
.multitextured())
313 if(!enablemtc
|| lastmtcbuf
!=lastvbuf
)
315 glClientActiveTexture_(GL_TEXTURE1_ARB
);
316 if(!enablemtc
) glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
317 if(lastmtcbuf
!=lastvbuf
)
319 vvertff
*vverts
= hasVBO
? 0 : (vvertff
*)vc
.vdata
;
320 glTexCoordPointer(2, GL_FLOAT
, ((vertmeshgroup
*)group
)->vertsize
, &vverts
->u
);
322 glClientActiveTexture_(GL_TEXTURE0_ARB
);
323 lastmtcbuf
= lastvbuf
;
327 else if(enablemtc
) disablemtc();
331 if(!enabletangents
|| lastnbuf
!=lastvbuf
)
333 if(!enabletangents
) glEnableVertexAttribArray_(1);
334 if(lastnbuf
!=lastvbuf
)
336 vvertbump
*vverts
= hasVBO
? 0 : (vvertbump
*)vc
.vdata
;
337 glVertexAttribPointer_(1, 4, GL_FLOAT
, GL_FALSE
, ((vertmeshgroup
*)group
)->vertsize
, &vverts
->tangent
.x
);
340 enabletangents
= true;
343 else if(enabletangents
) disabletangents();
345 if(renderpath
==R_FIXEDFUNCTION
&& (s
.scrollu
|| s
.scrollv
))
347 glMatrixMode(GL_TEXTURE
);
349 glTranslatef(s
.scrollu
*lastmillis
/1000.0f
, s
.scrollv
*lastmillis
/1000.0f
, 0);
351 if(s
.multitextured())
353 glActiveTexture_(GL_TEXTURE1_ARB
);
355 glTranslatef(s
.scrollu
*lastmillis
/1000.0f
, s
.scrollv
*lastmillis
/1000.0f
, 0);
360 if(hasDRE
) glDrawRangeElements_(GL_TRIANGLES
, minvert
, maxvert
, elen
, GL_UNSIGNED_SHORT
, &((vertmeshgroup
*)group
)->edata
[eoffset
]);
361 else glDrawElements(GL_TRIANGLES
, elen
, GL_UNSIGNED_SHORT
, &((vertmeshgroup
*)group
)->edata
[eoffset
]);
363 xtravertsva
+= numverts
;
365 if(renderpath
==R_FIXEDFUNCTION
&& !(as
->anim
&ANIM_NOSKIN
) && (s
.scrollu
|| s
.scrollv
))
367 if(s
.multitextured())
370 glActiveTexture_(GL_TEXTURE0_ARB
);
374 glMatrixMode(GL_MODELVIEW
);
386 tag() : name(NULL
) {}
387 ~tag() { DELETEA(name
); }
390 struct vertmeshgroup
: meshgroup
396 static const int MAXVBOCACHE
= 16;
397 vbocacheentry vbocache
[MAXVBOCACHE
];
401 bool vnorms
, vtangents
;
405 vertmeshgroup() : numframes(0), tags(NULL
), numtags(0), edata(NULL
), ebuf(0), vdata(NULL
)
409 virtual ~vertmeshgroup()
412 if(ebuf
) glDeleteBuffers_(1, &ebuf
);
415 DELETEA(vbocache
[i
].vdata
);
416 if(vbocache
[i
].vbuf
) glDeleteBuffers_(1, &vbocache
[i
].vbuf
);
421 int findtag(const char *name
)
423 loopi(numtags
) if(!strcmp(tags
[i
].name
, name
)) return i
;
427 virtual meshgroup
*allocate() { return new vertmeshgroup
; }
431 vertmeshgroup
&group
= *(vertmeshgroup
*)meshgroup::copy();
432 group
.numframes
= numframes
;
433 group
.numtags
= numtags
;
434 group
.tags
= new tag
[numframes
*numtags
];
435 memcpy(group
.tags
, tags
, numframes
*numtags
*sizeof(tag
));
436 loopi(numframes
*numtags
) if(group
.tags
[i
].name
) group
.tags
[i
].name
= newstring(group
.tags
[i
].name
);
440 int totalframes() const { return numframes
; }
442 void scaletags(const vec
&transdiff
, float scalediff
)
444 loopi(numframes
*numtags
)
446 matrix3x4
&m
= tags
[i
].transform
;
447 m
.X
.w
= (m
.X
.w
+transdiff
.x
)*scalediff
;
448 m
.Y
.w
= (m
.Y
.w
+transdiff
.y
)*scalediff
;
449 m
.Z
.w
= (m
.Z
.w
+transdiff
.z
)*scalediff
;
453 void concattagtransform(int frame
, int i
, const matrix3x4
&m
, matrix3x4
&n
)
455 n
.mul(m
, tags
[frame
*numtags
+ i
].transform
);
458 void calctagmatrix(int i
, const animstate
&as
, GLfloat
*matrix
)
460 const matrix3x4
&tag1
= tags
[as
.cur
.fr1
*numtags
+ i
].transform
,
461 &tag2
= tags
[as
.cur
.fr2
*numtags
+ i
].transform
;
462 #define ip(p1, p2, t) (p1+t*(p2-p1))
463 #define ip_ai_tag(c) ip( ip( tag1p.c, tag2p.c, as.prev.t), ip( tag1.c, tag2.c, as.cur.t), as.interp)
466 const matrix3x4
&tag1p
= tags
[as
.prev
.fr1
*numtags
+ i
].transform
,
467 &tag2p
= tags
[as
.prev
.fr2
*numtags
+ i
].transform
;
470 matrix
[4*j
+0] = ip_ai_tag(X
[j
]);
471 matrix
[4*j
+1] = ip_ai_tag(Y
[j
]);
472 matrix
[4*j
+2] = ip_ai_tag(Z
[j
]);
477 matrix
[4*j
+0] = ip(tag1
.X
[j
], tag2
.X
[j
], as
.cur
.t
);
478 matrix
[4*j
+1] = ip(tag1
.Y
[j
], tag2
.Y
[j
], as
.cur
.t
);
479 matrix
[4*j
+2] = ip(tag1
.Z
[j
], tag2
.Z
[j
], as
.cur
.t
);
483 matrix
[3] = matrix
[7] = matrix
[11] = 0.0f
;
487 void genvbo(bool norms
, bool tangents
, vbocacheentry
&vc
)
491 if(!vc
.vbuf
) glGenBuffers_(1, &vc
.vbuf
);
496 #define ALLOCVDATA(vdata) \
500 vdata = new uchar[vlen*vertsize]; \
501 loopv(meshes) ((vertmesh *)meshes[i])->filltc(vdata, vertsize); \
503 if(!vc
.vdata
) ALLOCVDATA(vc
.vdata
);
510 vtangents
= tangents
;
511 vertsize
= tangents
? sizeof(vvertbump
) : (norms
? sizeof(vvert
) : sizeof(vvertff
));
515 loopv(meshes
) vlen
+= ((vertmesh
*)meshes
[i
])->genvbo(idxs
, vlen
);
517 if(hasVBO
) ALLOCVDATA(vdata
);
518 else ALLOCVDATA(vc
.vdata
);
522 if(hasVBO
) glBindBuffer_(GL_ARRAY_BUFFER_ARB
, vc
.vbuf
);
523 #define GENVBO(type) \
526 vector<type> vverts; \
527 loopv(meshes) vlen += ((vertmesh *)meshes[i])->genvbo(idxs, vlen, vverts); \
528 if(hasVBO) glBufferData_(GL_ARRAY_BUFFER_ARB, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW_ARB); \
532 vc.vdata = new uchar[vverts.length()*sizeof(type)]; \
533 memcpy(vc.vdata, vverts.getbuf(), vverts.length()*sizeof(type)); \
536 if(tangents
) GENVBO(vvertbump
);
537 else if(norms
) GENVBO(vvert
);
538 else GENVBO(vvertff
);
543 glGenBuffers_(1, &ebuf
);
544 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB
, ebuf
);
545 glBufferData_(GL_ELEMENT_ARRAY_BUFFER_ARB
, idxs
.length()*sizeof(ushort
), idxs
.getbuf(), GL_STATIC_DRAW_ARB
);
549 edata
= new ushort
[idxs
.length()];
550 memcpy(edata
, idxs
.getbuf(), idxs
.length()*sizeof(ushort
));
556 void bindvbo(const animstate
*as
, vbocacheentry
&vc
)
558 vvert
*vverts
= hasVBO
? 0 : (vvert
*)vc
.vdata
;
559 if(hasVBO
&& lastebuf
!=ebuf
)
561 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB
, ebuf
);
564 if(lastvbuf
!= (hasVBO
? (void *)(size_t)vc
.vbuf
: vc
.vdata
))
566 if(hasVBO
) glBindBuffer_(GL_ARRAY_BUFFER_ARB
, vc
.vbuf
);
567 if(!lastvbuf
) glEnableClientState(GL_VERTEX_ARRAY
);
568 glVertexPointer(3, GL_FLOAT
, vertsize
, &vverts
->pos
);
570 lastvbuf
= hasVBO
? (void *)(size_t)vc
.vbuf
: vc
.vdata
;
571 if(as
->anim
&ANIM_NOSKIN
)
573 if(enabletc
) disabletc();
575 else if(!enabletc
|| lasttcbuf
!=lastvbuf
)
577 if(vnorms
|| vtangents
)
579 if(!enabletc
) glEnableClientState(GL_NORMAL_ARRAY
);
580 if(lasttcbuf
!=lastvbuf
) glNormalPointer(GL_FLOAT
, vertsize
, &vverts
->norm
);
582 if(!enabletc
) glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
583 if(lasttcbuf
!=lastvbuf
) glTexCoordPointer(2, GL_FLOAT
, vertsize
, &vverts
->u
);
584 lasttcbuf
= lastvbuf
;
593 vbocacheentry
&c
= vbocache
[i
];
594 if(c
.vbuf
) { glDeleteBuffers_(1, &c
.vbuf
); c
.vbuf
= 0; }
598 if(hasVBO
) { if(ebuf
) { glDeleteBuffers_(1, &ebuf
); ebuf
= 0; } }
600 lastvbuf
= lasttcbuf
= lastmtcbuf
= lastnbuf
= NULL
;
604 void render(const animstate
*as
, float pitch
, const vec
&axis
, part
*p
)
606 bool norms
= false, tangents
= false;
609 if(p
->skins
[i
].normals()) norms
= true;
610 if(p
->skins
[i
].tangents()) tangents
= true;
612 if(norms
!=vnorms
|| tangents
!=vtangents
) cleanup();
613 vbocacheentry
*vc
= NULL
;
614 if(numframes
<=1) vc
= vbocache
;
619 vbocacheentry
&c
= vbocache
[i
];
620 if(hasVBO
? !c
.vbuf
: !c
.vdata
) continue;
621 if(c
.as
==*as
) { vc
= &c
; break; }
623 if(!vc
) loopi(MAXVBOCACHE
) { vc
= &vbocache
[i
]; if((hasVBO
? !vc
->vbuf
: !vc
->vdata
) || vc
->millis
< lastmillis
) break; }
625 if(hasVBO
? !vc
->vbuf
: !vc
->vdata
) genvbo(norms
, tangents
, *vc
);
631 vc
->millis
= lastmillis
;
634 vertmesh
&m
= *(vertmesh
*)meshes
[i
];
635 m
.interpverts(*as
, norms
, tangents
, (hasVBO
? vdata
: vc
->vdata
) + m
.voffset
*vertsize
, p
->skins
[i
]);
639 glBindBuffer_(GL_ARRAY_BUFFER_ARB
, vc
->vbuf
);
640 glBufferData_(GL_ARRAY_BUFFER_ARB
, vlen
*vertsize
, vdata
, GL_STREAM_DRAW_ARB
);
643 vc
->millis
= lastmillis
;
647 loopv(meshes
) ((vertmesh
*)meshes
[i
])->render(as
, p
->skins
[i
], *vc
);
649 loopv(p
->links
) calctagmatrix(p
->links
[i
].tag
, *as
, p
->links
[i
].matrix
);
653 vertmodel(const char *name
) : animmodel(name
)