Capture the Flag, 2008 Jun 17
[SauerbratenRemote.git] / src / engine / vertmodel.h
blob43ceb9ef709aeb55bdf3e46637def08545cbd8cb
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]; };
11 struct vbocacheentry
13 uchar *vdata;
14 GLuint vbuf;
15 animstate as;
16 int millis;
18 vbocacheentry() : vdata(NULL), vbuf(0) { as.cur.fr1 = as.prev.fr1 = -1; }
21 struct vertmesh : mesh
23 vert *verts;
24 tcvert *tcverts;
25 bumpvert *bumpverts;
26 tri *tris;
27 int numverts, numtris;
29 int voffset, eoffset, elen;
30 ushort minvert, maxvert;
32 vertmesh() : verts(0), tcverts(0), bumpverts(0), tris(0)
36 virtual ~vertmesh()
38 DELETEA(verts);
39 DELETEA(tcverts);
40 DELETEA(bumpverts);
41 DELETEA(tris);
44 virtual mesh *allocate() { return new vertmesh; }
46 mesh *copy()
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));
54 m.numtris = numtris;
55 m.tris = new tri[numtris];
56 memcpy(m.tris, tris, numtris*sizeof(tri));
57 if(bumpverts)
59 m.bumpverts = new bumpvert[numverts];
60 memcpy(m.bumpverts, bumpverts, numverts*sizeof(bumpvert));
62 else m.bumpverts = NULL;
63 return &m;
66 void scaleverts(const vec &transdiff, float scalediff)
68 loopi(((vertmeshgroup *)group)->numframes*numverts) verts[i].pos.add(transdiff).mul(scalediff);
71 void calctangents()
73 if(bumpverts) return;
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];
80 loopi(numtris)
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);
90 e1.sub(v0);
91 e2.sub(v0);
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;
97 vec u(e1), v(e2);
98 u.mul(v2).sub(vec(e2).mul(v1)).mul(scale);
99 v.mul(u1).sub(vec(e1).mul(u2)).mul(scale);
101 loopj(3)
103 tangent[t.vert[j]].add(u);
104 bitangent[t.vert[j]].add(v);
107 bumpvert *fbumpverts = &bumpverts[k*numverts];
108 loopi(numverts)
110 const vec &n = fverts[i].norm,
111 &t = tangent[i],
112 &bt = bitangent[i];
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;
118 delete[] tangent;
121 void calcbb(int frame, vec &bbmin, vec &bbmax, const matrix3x4 &m)
123 vert *fverts = &verts[frame*numverts];
124 loopj(numverts)
126 vec v = m.transform(fverts[j].pos);
127 loopi(3)
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];
138 loopj(numtris)
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]];
148 t.tc[0] = av.u;
149 t.tc[1] = av.v;
150 t.tc[2] = bv.u;
151 t.tc[3] = bv.v;
152 t.tc[4] = cv.u;
153 t.tc[5] = cv.v;
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)
174 vv.pos = v.pos;
175 vv.u = tc.u;
176 vv.v = tc.v;
179 static inline void assignvert(vvert &vv, int j, tcvert &tc, vert &v)
181 vv.pos = v.pos;
182 vv.norm = v.norm;
183 vv.u = tc.u;
184 vv.v = tc.v;
187 inline void assignvert(vvertbump &vv, int j, tcvert &tc, vert &v)
189 vv.pos = v.pos;
190 vv.norm = v.norm;
191 vv.u = tc.u;
192 vv.v = tc.v;
193 if(bumpverts)
195 vv.tangent = bumpverts[j].tangent;
196 vv.bitangent = bumpverts[j].bitangent;
200 template<class T>
201 int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts)
203 voffset = offset;
204 eoffset = idxs.length();
205 minvert = 0xFFFF;
206 loopi(numtris)
208 tri &t = tris[i];
209 loopj(3)
211 int index = t.vert[j];
212 tcvert &tc = tcverts[index];
213 vert &v = verts[index];
214 loopvk(vverts)
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);
220 found:;
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)
231 voffset = offset;
232 eoffset = idxs.length();
233 loopi(numtris)
235 tri &t = tris[i];
236 loopj(3) idxs.add(voffset+t.vert[j]);
238 minvert = voffset;
239 maxvert = voffset + numverts-1;
240 elen = idxs.length()-eoffset;
241 return numverts;
244 void filltc(uchar *vdata, size_t stride)
246 vdata = (uchar *)&((vvertff *)&vdata[voffset*stride])->u;
247 loopi(numverts)
249 *(tcvert *)vdata = tcverts[i];
250 vdata += stride;
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) \
271 loopi(numverts) \
273 type &v = ((type *)vdata)[i]; \
274 body; \
276 if(tangents)
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; })
285 else if(norms)
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)
292 #undef iploop
293 #undef ip
294 #undef ip_v
295 #undef ip_v_ai
296 #undef ip_pos
297 #undef ip_pos_ai
298 #undef ip_norm
299 #undef ip_norm_ai
300 #undef ip_b_ai
301 #undef ip_tangent
302 #undef ip_tangent_ai
305 void render(const animstate *as, skin &s, vbocacheentry &vc)
307 s.bind(this, as);
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;
324 enablemtc = true;
327 else if(enablemtc) disablemtc();
329 if(s.tangents())
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);
339 lastnbuf = lastvbuf;
340 enabletangents = true;
343 else if(enabletangents) disabletangents();
345 if(renderpath==R_FIXEDFUNCTION && (s.scrollu || s.scrollv))
347 glMatrixMode(GL_TEXTURE);
348 glPushMatrix();
349 glTranslatef(s.scrollu*lastmillis/1000.0f, s.scrollv*lastmillis/1000.0f, 0);
351 if(s.multitextured())
353 glActiveTexture_(GL_TEXTURE1_ARB);
354 glPushMatrix();
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]);
362 glde++;
363 xtravertsva += numverts;
365 if(renderpath==R_FIXEDFUNCTION && !(as->anim&ANIM_NOSKIN) && (s.scrollu || s.scrollv))
367 if(s.multitextured())
369 glPopMatrix();
370 glActiveTexture_(GL_TEXTURE0_ARB);
373 glPopMatrix();
374 glMatrixMode(GL_MODELVIEW);
377 return;
381 struct tag
383 char *name;
384 matrix3x4 transform;
386 tag() : name(NULL) {}
387 ~tag() { DELETEA(name); }
390 struct vertmeshgroup : meshgroup
392 int numframes;
393 tag *tags;
394 int numtags;
396 static const int MAXVBOCACHE = 16;
397 vbocacheentry vbocache[MAXVBOCACHE];
399 ushort *edata;
400 GLuint ebuf;
401 bool vnorms, vtangents;
402 int vlen, vertsize;
403 uchar *vdata;
405 vertmeshgroup() : numframes(0), tags(NULL), numtags(0), edata(NULL), ebuf(0), vdata(NULL)
409 virtual ~vertmeshgroup()
411 DELETEA(tags);
412 if(ebuf) glDeleteBuffers_(1, &ebuf);
413 loopi(MAXVBOCACHE)
415 DELETEA(vbocache[i].vdata);
416 if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
418 DELETEA(vdata);
421 int findtag(const char *name)
423 loopi(numtags) if(!strcmp(tags[i].name, name)) return i;
424 return -1;
427 virtual meshgroup *allocate() { return new vertmeshgroup; }
429 meshgroup *copy()
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);
437 return &group;
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)
464 if(as.interp<1)
466 const matrix3x4 &tag1p = tags[as.prev.fr1*numtags + i].transform,
467 &tag2p = tags[as.prev.fr2*numtags + i].transform;
468 loopj(4)
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]);
475 else loopj(4)
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);
481 #undef ip_ai_tag
482 #undef ip
483 matrix[3] = matrix[7] = matrix[11] = 0.0f;
484 matrix[15] = 1.0f;
487 void genvbo(bool norms, bool tangents, vbocacheentry &vc)
489 if(hasVBO)
491 if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
492 if(ebuf) return;
494 else if(edata)
496 #define ALLOCVDATA(vdata) \
497 do \
499 DELETEA(vdata); \
500 vdata = new uchar[vlen*vertsize]; \
501 loopv(meshes) ((vertmesh *)meshes[i])->filltc(vdata, vertsize); \
502 } while(0)
503 if(!vc.vdata) ALLOCVDATA(vc.vdata);
504 return;
507 vector<ushort> idxs;
509 vnorms = norms;
510 vtangents = tangents;
511 vertsize = tangents ? sizeof(vvertbump) : (norms ? sizeof(vvert) : sizeof(vvertff));
512 vlen = 0;
513 if(numframes>1)
515 loopv(meshes) vlen += ((vertmesh *)meshes[i])->genvbo(idxs, vlen);
516 DELETEA(vdata);
517 if(hasVBO) ALLOCVDATA(vdata);
518 else ALLOCVDATA(vc.vdata);
520 else
522 if(hasVBO) glBindBuffer_(GL_ARRAY_BUFFER_ARB, vc.vbuf);
523 #define GENVBO(type) \
524 do \
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); \
529 else \
531 DELETEA(vc.vdata); \
532 vc.vdata = new uchar[vverts.length()*sizeof(type)]; \
533 memcpy(vc.vdata, vverts.getbuf(), vverts.length()*sizeof(type)); \
535 } while(0)
536 if(tangents) GENVBO(vvertbump);
537 else if(norms) GENVBO(vvert);
538 else GENVBO(vvertff);
541 if(hasVBO)
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);
547 else
549 edata = new ushort[idxs.length()];
550 memcpy(edata, idxs.getbuf(), idxs.length()*sizeof(ushort));
552 #undef GENVBO
553 #undef ALLOCVDATA
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);
562 lastebuf = 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;
585 enabletc = true;
589 void cleanup()
591 loopi(MAXVBOCACHE)
593 vbocacheentry &c = vbocache[i];
594 if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
595 DELETEA(c.vdata);
596 c.as.cur.fr1 = -1;
598 if(hasVBO) { if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; } }
599 else DELETEA(vdata);
600 lastvbuf = lasttcbuf = lastmtcbuf = lastnbuf = NULL;
601 lastebuf = 0;
604 void render(const animstate *as, float pitch, const vec &axis, part *p)
606 bool norms = false, tangents = false;
607 loopv(p->skins)
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;
615 else
617 loopi(MAXVBOCACHE)
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);
626 if(numframes>1)
628 if(vc->as!=*as)
630 vc->as = *as;
631 vc->millis = lastmillis;
632 loopv(meshes)
634 vertmesh &m = *(vertmesh *)meshes[i];
635 m.interpverts(*as, norms, tangents, (hasVBO ? vdata : vc->vdata) + m.voffset*vertsize, p->skins[i]);
637 if(hasVBO)
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;
646 bindvbo(as, *vc);
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)