4
VARP(animationinterpolationtime, 0, 150, 1000);
6
model *loadingmodel = NULL;
9
#include "modelcache.h"
10
#include "vertmodel.h"
14
#define checkmdl if(!loadingmodel) { conoutf("not loading a model"); return; }
16
void mdlcullface(int cullface)
19
loadingmodel->cullface = cullface!=0;
22
COMMAND(mdlcullface, ARG_1INT);
24
void mdlvertexlight(int vertexlight)
27
loadingmodel->vertexlight = vertexlight!=0;
30
COMMAND(mdlvertexlight, ARG_1INT);
32
void mdltranslucent(int translucency)
35
loadingmodel->translucency = translucency/100.0f;
38
COMMAND(mdltranslucent, ARG_1INT);
40
void mdlalphatest(int alphatest)
43
loadingmodel->alphatest = alphatest/100.0f;
46
COMMAND(mdlalphatest, ARG_1INT);
48
void mdlscale(int percent)
52
if(percent>0) scale = percent/100.0f;
53
else if(percent<0) scale = 0.0f;
54
loadingmodel->scale = scale;
57
COMMAND(mdlscale, ARG_1INT);
59
void mdltrans(char *x, char *y, char *z)
62
loadingmodel->translate = vec(atof(x), atof(y), atof(z));
65
COMMAND(mdltrans, ARG_3STR);
67
void mdlshadowdist(int dist)
70
loadingmodel->shadowdist = dist;
73
COMMAND(mdlshadowdist, ARG_1INT);
75
void mdlcachelimit(int limit)
78
loadingmodel->cachelimit = limit;
81
COMMAND(mdlcachelimit, ARG_1INT);
83
vector<mapmodelinfo> mapmodels;
85
void mapmodel(char *rad, char *h, char *zoff, char *snap, char *name)
87
mapmodelinfo &mmi = mapmodels.add();
90
mmi.zoff = atoi(zoff);
91
s_sprintf(mmi.name)("mapmodels/%s", name);
96
if(execcontext==IEXC_MAPCFG) mapmodels.setsize(0);
99
mapmodelinfo &getmminfo(int i) { return mapmodels.inrange(i) ? mapmodels[i] : *(mapmodelinfo *)0; }
101
COMMAND(mapmodel, ARG_5STR);
102
COMMAND(mapmodelreset, ARG_NONE);
104
hashtable<const char *, model *> mdllookup;
106
model *loadmodel(const char *name, int i)
110
if(!mapmodels.inrange(i)) return NULL;
111
mapmodelinfo &mmi = mapmodels[i];
112
if(mmi.m) return mmi.m;
115
model **mm = mdllookup.access(name);
135
mdllookup.access(m->name(), m);
137
if(mapmodels.inrange(i) && !mapmodels[i].m) mapmodels[i].m = m;
143
enumerate(mdllookup, model *, m, m->cleanup());
146
VARP(dynshadow, 0, 40, 100);
147
VARP(dynshadowdecay, 0, 1000, 3000);
152
int anim, varseed, tex;
153
float yaw, pitch, speed;
162
vector<batchedmodel> batched;
164
static vector<modelbatch *> batches;
165
static vector<modelattach> modelattached;
166
static int numbatches = -1;
168
void startmodelbatches()
171
modelattached.setsizenodelete(0);
174
batchedmodel &addbatchedmodel(model *m)
176
modelbatch *b = NULL;
177
if(m->batch>=0 && m->batch<numbatches && batches[m->batch]->m==m) b = batches[m->batch];
180
if(numbatches<batches.length())
182
b = batches[numbatches];
183
b->batched.setsizenodelete(0);
185
else b = batches.add(new modelbatch);
187
m->batch = numbatches++;
189
return b->batched.add();
192
void renderbatchedmodel(model *m, batchedmodel &b)
194
modelattach *a = NULL;
195
if(b.attached>=0) a = &modelattached[b.attached];
199
m->render(b.anim|ANIM_NOSKIN, b.varseed, b.speed, b.basetime, b.o, b.yaw, b.pitch, b.d, a, b.scale);
203
int x = (int)b.o.x, y = (int)b.o.y;
207
glColor3ub(s->r, s->g, s->b);
209
else glColor3f(1, 1, 1);
213
if(b.anim&ANIM_TRANSLUCENT)
215
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
216
m->render(b.anim|ANIM_NOSKIN, b.varseed, b.speed, b.basetime, b.o, b.yaw, b.pitch, b.d, a, b.scale);
217
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
219
glDepthFunc(GL_LEQUAL);
221
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
224
glGetFloatv(GL_CURRENT_COLOR, color);
225
glColor4f(color[0], color[1], color[2], m->translucency);
228
m->render(b.anim, b.varseed, b.speed, b.basetime, b.o, b.yaw, b.pitch, b.d, a, b.scale);
230
if(b.anim&ANIM_TRANSLUCENT)
232
glDepthFunc(GL_LESS);
237
void renderbatchedmodelshadow(model *m, batchedmodel &b)
239
int x = (int)b.o.x, y = (int)b.o.y;
240
if(OUTBORD(x, y)) return;
242
vec center(b.o.x, b.o.y, s->floor);
243
if(s->type==FHF) center.z -= s->vdelta/4.0f;
244
if(dynshadowquad && center.z-0.1f>b.o.z) return;
246
modelattach *a = NULL;
247
if(b.attached>=0) a = &modelattached[b.attached];
248
float intensity = dynshadow/100.0f;
249
if(dynshadowdecay) switch(b.anim&ANIM_INDEX)
252
case ANIM_LYING_DEAD:
253
intensity *= max(1.0f - float(lastmillis - b.basetime)/dynshadowdecay, 0.0f);
256
glColor4f(0, 0, 0, intensity);
257
m->rendershadow(b.anim, b.varseed, b.speed, b.basetime, dynshadowquad ? center : b.o, b.yaw, a);
260
static int sortbatchedmodels(const batchedmodel *x, const batchedmodel *y)
262
if(x->tex < y->tex) return -1;
263
if(x->tex > y->tex) return 1;
267
struct translucentmodel
270
batchedmodel *batched;
274
static int sorttranslucentmodels(const translucentmodel *x, const translucentmodel *y)
276
if(x->dist > y->dist) return -1;
277
if(x->dist < y->dist) return 1;
281
void clearmodelbatches()
286
void endmodelbatches(bool flush)
288
vector<translucentmodel> translucent;
291
modelbatch &b = *batches[i];
292
if(b.batched.empty()) continue;
293
loopvj(b.batched) if(b.batched[j].tex) { b.batched.sort(sortbatchedmodels); break; }
297
batchedmodel &bm = b.batched[j];
298
if(bm.anim&ANIM_TRANSLUCENT)
300
translucentmodel &tm = translucent.add();
303
tm.dist = camera1->o.dist(bm.o);
306
renderbatchedmodel(b.m, bm);
308
if(dynshadow && b.m->hasshadows() && (!reflecting || refracting) && (!stencilshadow || !hasstencil || stencilbits < 8))
312
batchedmodel &bm = b.batched[j];
313
if(bm.anim&ANIM_TRANSLUCENT) continue;
314
renderbatchedmodelshadow(b.m, bm);
319
if(translucent.length())
321
translucent.sort(sorttranslucentmodels);
322
model *lastmodel = NULL;
325
translucentmodel &tm = translucent[i];
328
if(lastmodel) lastmodel->endrender();
329
(lastmodel = tm.m)->startrender();
331
renderbatchedmodel(tm.m, *tm.batched);
333
if(lastmodel) lastmodel->endrender();
335
if(flush) clearmodelbatches();
338
VAR(dbgmbatch, 0, 0, 1);
340
void rendermodel(const char *mdl, int anim, int tex, float rad, const vec &o, float yaw, float pitch, float speed, int basetime, playerent *d, modelattach *a, float scale)
342
model *m = loadmodel(mdl);
343
if(!m || (stenciling && (m->shadowdist <= 0 || anim&ANIM_TRANSLUCENT))) return;
347
if(!rad) rad = m->radius;
348
if(isoccluded(camera1->o.x, camera1->o.y, o.x-rad, o.y-rad, rad*2)) return;
351
if(stenciling && d && !raycubelos(camera1->o, o, d->radius))
354
target.z += d->eyeheight;
355
if(!raycubelos(camera1->o, target, d->radius)) return;
359
if(d) switch(anim&ANIM_INDEX)
362
case ANIM_LYING_DEAD: varseed = (int)(size_t)d + d->lastpain; break;
363
default: varseed = (int)(size_t)d + d->lastaction; break;
366
if(a) for(int i = 0; a[i].tag; i++)
368
if(a[i].name) a[i].m = loadmodel(a[i].name);
369
//if(a[i].m && a[i].m->type()!=m->type()) a[i].m = NULL;
372
if(numbatches>=0 && !dbgmbatch)
374
batchedmodel &b = addbatchedmodel(m);
382
b.basetime = basetime;
384
b.attached = a ? modelattached.length() : -1;
385
if(a) for(int i = 0;; i++) { modelattached.add(a[i]); if(!a[i].tag) break; }
393
m->render(anim|ANIM_NOSKIN, varseed, speed, basetime, o, yaw, pitch, d, a, scale);
400
int x = (int)o.x, y = (int)o.y;
404
if(!(anim&ANIM_TRANSLUCENT) && dynshadow && m->hasshadows() && (!reflecting || refracting) && (!stencilshadow || !hasstencil || stencilbits < 8))
406
vec center(o.x, o.y, s->floor);
407
if(s->type==FHF) center.z -= s->vdelta/4.0f;
408
if(!dynshadowquad || center.z-0.1f<=o.z)
411
float intensity = dynshadow/100.0f;
412
if(dynshadowdecay) switch(anim&ANIM_INDEX)
415
case ANIM_LYING_DEAD:
416
intensity *= max(1.0f - float(lastmillis - basetime)/dynshadowdecay, 0.0f);
419
glColor4f(0, 0, 0, intensity);
420
m->rendershadow(anim, varseed, speed, basetime, dynshadowquad ? center : o, yaw, a);
423
glColor3ub(s->r, s->g, s->b);
425
else glColor3f(1, 1, 1);
429
if(anim&ANIM_TRANSLUCENT)
431
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
432
m->render(anim|ANIM_NOSKIN, varseed, speed, basetime, o, yaw, pitch, d, a, scale);
433
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
435
glDepthFunc(GL_LEQUAL);
437
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440
glGetFloatv(GL_CURRENT_COLOR, color);
441
glColor4f(color[0], color[1], color[2], m->translucency);
444
m->render(anim, varseed, speed, basetime, o, yaw, pitch, d, a, scale);
446
if(anim&ANIM_TRANSLUCENT)
448
glDepthFunc(GL_LESS);
455
int findanim(const char *name)
457
const char *names[] = { "idle", "run", "attack", "pain", "jump", "land", "flipoff", "salute", "taunt", "wave", "point", "crouch idle", "crouch walk", "crouch attack", "crouch pain", "crouch death", "death", "lying dead", "flag", "gun idle", "gun shoot", "gun reload", "gun throw", "mapmodel", "trigger", "decay", "all" };
458
loopi(sizeof(names)/sizeof(names[0])) if(!strcmp(name, names[i])) return i;
462
void loadskin(const char *dir, const char *altdir, Texture *&skin) // model skin sharing
464
#define ifnoload if((skin = textureload(path))==notexture)
465
s_sprintfd(path)("packages/models/%s/skin.jpg", dir);
468
strcpy(path+strlen(path)-3, "png");
471
s_sprintf(path)("packages/models/%s/skin.jpg", altdir);
474
strcpy(path+strlen(path)-3, "png");
481
void preload_playermodels()
483
model *playermdl = loadmodel("playermodels");
484
if(dynshadow && playermdl) playermdl->genshadows(8.0f, 4.0f);
487
s_sprintfd(vwep)("weapons/%s/world", guns[i].modelname);
488
model *vwepmdl = loadmodel(vwep);
489
if(dynshadow && vwepmdl) vwepmdl->genshadows(8.0f, 4.0f);
493
void preload_entmodels()
495
extern const char *entmdlnames[];
496
loopi(I_AKIMBO-I_CLIPS+1)
498
model *mdl = loadmodel(entmdlnames[i]);
499
if(dynshadow && mdl) mdl->genshadows(8.0f, 2.0f);
501
static const char *bouncemdlnames[] = { "misc/gib01", "misc/gib02", "misc/gib03", "weapons/grenade/static" };
502
loopi(sizeof(bouncemdlnames)/sizeof(bouncemdlnames[0]))
504
model *mdl = loadmodel(bouncemdlnames[i]);
505
if(dynshadow && mdl) mdl->genshadows(8.0f, 2.0f);
509
void preload_mapmodels()
514
if(e.type!=MAPMODEL || !mapmodels.inrange(e.attr2)) continue;
515
if(!loadmodel(NULL, e.attr2)) continue;
516
if(e.attr4) lookuptexture(e.attr4);
520
VAR(dbghbox, 0, 0, 1);
522
void renderhbox(playerent *d)
524
glDisable(GL_TEXTURE_2D);
527
float y = d->yaw*RAD, p = (d->pitch/4+90)*RAD, c = cosf(p);
528
vec bottom(d->o), up(sinf(y)*c, -cosf(y)*c, sinf(p)), top(up);
529
bottom.z -= d->eyeheight;
530
top.mul(d->eyeheight + d->aboveeye).add(bottom);
532
if(d->state==CS_ALIVE && d->head.x >= 0)
534
glBegin(GL_LINE_LOOP);
538
pos.rotate(2*M_PI*i/8.0f, camdir).mul(HEADSIZE).add(d->head);
544
glVertex3fv(bottom.v);
545
glVertex3fv(d->head.v);
550
spoke.orthogonal(up);
551
spoke.normalize().mul(d->radius);
553
glBegin(GL_LINE_LOOP);
557
pos.rotate(2*M_PI*i/8.0f, up).add(top);
561
glBegin(GL_LINE_LOOP);
565
pos.rotate(2*M_PI*i/8.0f, up).add(bottom);
573
pos.rotate(2*M_PI*i/8.0f, up).add(bottom);
575
pos.sub(bottom).add(top);
580
glEnable(GL_TEXTURE_2D);
583
void renderclient(playerent *d, const char *mdlname, const char *vwepname, int tex)
585
int varseed = (int)(size_t)d;
586
int anim = ANIM_IDLE|ANIM_LOOP;
590
int basetime = -((int)(size_t)d&0xFFF);
591
if(d->state==CS_DEAD)
593
if(d==player1 && d->allowmove()) return;
594
loopv(bounceents) if(bounceents[i]->bouncetype==BT_GIB && bounceents[i]->owner==d) return;
597
varseed += d->lastpain;
598
basetime = d->lastpain;
599
int t = lastmillis-d->lastpain;
600
if(t<0 || t>20000) return;
603
anim = ANIM_LYING_DEAD|ANIM_NOINTERP|ANIM_LOOP;
606
o.z -= t*t/10000000000.0f*t;
609
else if(d->state==CS_EDITING) { anim = ANIM_JUMP|ANIM_END; }
610
else if(d->state==CS_LAGGED) { anim = ANIM_SALUTE|ANIM_LOOP|ANIM_TRANSLUCENT; }
611
else if(lastmillis-d->lastpain<300) { anim = d->crouching ? ANIM_CROUCH_PAIN : ANIM_PAIN; speed = 300.0f/4; varseed += d->lastpain; basetime = d->lastpain; }
612
else if(!d->onfloor && d->timeinair>50) { anim = ANIM_JUMP|ANIM_END; }
613
else if(d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction<300 && d->lastpain < d->lastaction) { anim = d->crouching ? ANIM_CROUCH_ATTACK : ANIM_ATTACK; speed = 300.0f/8; basetime = d->lastaction; }
614
else if(!d->move && !d->strafe) { anim = (d->crouching ? ANIM_CROUCH_IDLE : ANIM_IDLE)|ANIM_LOOP; }
615
else { anim = (d->crouching ? ANIM_CROUCH_WALK : ANIM_RUN)|ANIM_LOOP; speed = 1860/d->maxspeed; }
620
a[numattach].name = vwepname;
621
a[numattach].tag = "tag_weapon";
625
if(!stenciling && !reflecting && !refracting)
627
if(d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction < d->weaponsel->flashtime())
628
anim |= ANIM_PARTICLE;
629
if(d != player1 && d->state==CS_ALIVE)
631
d->head = vec(-1, -1, -1);
632
a[numattach].tag = "tag_head";
633
a[numattach].pos = &d->head;
637
// FIXME: while networked my state as spectator seems to stay CS_DEAD, not CS_SPECTATE
638
// flowtron: I fixed this for following at least (see followplayer())
639
if(player1->isspectating() && d->clientnum == player1->followplayercn && player1->spectatemode == SM_FOLLOW3RD_TRANSPARENT)
641
anim |= ANIM_TRANSLUCENT; // see through followed player
642
if(stenciling) return;
644
rendermodel(mdlname, anim|ANIM_DYNALLOC, tex, 1.5f, o, d->yaw+90, d->pitch/4, speed, basetime, d, a);
645
if(!stenciling && !reflecting && !refracting)
647
if(isteam(player1->team, d->team)) renderaboveheadicon(d);
648
if(dbghbox) renderhbox(d);
652
VARP(teamdisplaymode, 0, 1, 2);
654
#define SKINBASE "packages/models/playermodels"
655
VARP(hidecustomskins, 0, 0, 2);
656
static cvector playerskinlist;
658
const char *getclientskin(const char *name, const char *suf)
661
int suflen = (int)strlen(suf), namelen = (int)strlen(name);
662
const char *s, *r = NULL;
663
loopv(playerskinlist)
665
s = playerskinlist[i];
666
int sl = (int)strlen(s) - suflen;
667
if(sl > 0 && !strcmp(s + sl, suf))
669
if(namelen == sl && !strncmp(name, s, namelen)) return s; // exact match
674
if(strstr(name, tmp)) r = s; // partial match
681
void updateclientname(playerent *d)
683
static bool gotlist = false;
684
if(!gotlist) listfiles(SKINBASE "/custom", "jpg", playerskinlist);
686
if(!d || !playerskinlist.length()) return;
687
d->skin_noteam = getclientskin(d->name, "_ffa");
688
d->skin_cla = getclientskin(d->name, "_cla");
689
d->skin_rvsf = getclientskin(d->name, "_rvsf");
692
void renderclient(playerent *d)
695
const char *cs = NULL, *skinbase = SKINBASE;
696
int team = team_int(d->team);
697
int skinid = 1 + max(0, min(d->skin, (team==TEAM_CLA ? 3 : 5)));
699
if(hidecustomskins == 0 || (hidecustomskins == 1 && !m_teammode))
701
cs = team ? d->skin_rvsf : d->skin_cla;
702
if(!m_teammode && d->skin_noteam) cs = d->skin_noteam;
705
s_sprintf(skin)("%s/custom/%s.jpg", skinbase, cs);
708
if(!m_teammode || !teamdisplaymode) s_sprintf(skin)("%s/%s/%02i.jpg", skinbase, team_string(team), skinid);
709
else switch(teamdisplaymode)
711
case 1: s_sprintf(skin)("%s/%s/%02i_%svest.jpg", skinbase, team_string(team), skinid, team ? "blue" : "red"); break;
712
case 2: default: s_sprintf(skin)("%s/%s/%s.jpg", skinbase, team_string(team), team ? "blue" : "red"); break;
716
if(d->weaponsel) s_sprintf(vwep)("weapons/%s/world", d->weaponsel->info.modelname);
718
renderclient(d, "playermodels", vwep[0] ? vwep : NULL, -(int)textureload(skin)->id);
724
loopv(players) if((d = players[i]) && d->state!=CS_SPAWNING && (!player1->isspectating() || player1->spectatemode != SM_FOLLOW1ST || player1->followplayercn != i)) renderclient(d);
725
if(player1->state==CS_DEAD || (reflecting && !refracting)) renderclient(player1);