4 VARP(oqdynent
, 0, 1, 1);
5 VARP(animationinterpolationtime
, 0, 150, 1000);
6 VARP(orientinterpolationtime
, 0, 75, 1000);
8 model
*loadingmodel
= NULL
;
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
, "i");
24 void mdlcollide(int *collide
)
27 loadingmodel
->collide
= *collide
!=0;
30 COMMAND(mdlcollide
, "i");
32 void mdlellipsecollide(int *collide
)
35 loadingmodel
->ellipsecollide
= *collide
!=0;
38 COMMAND(mdlellipsecollide
, "i");
40 void mdlspec(int *percent
)
44 if(*percent
>0) spec
= *percent
/100.0f
;
45 else if(*percent
<0) spec
= 0.0f
;
46 loadingmodel
->setspec(spec
);
49 COMMAND(mdlspec
, "i");
51 void mdlambient(int *percent
)
55 if(*percent
>0) ambient
= *percent
/100.0f
;
56 else if(*percent
<0) ambient
= 0.0f
;
57 loadingmodel
->setambient(ambient
);
60 COMMAND(mdlambient
, "i");
62 void mdlalphatest(float *cutoff
)
65 loadingmodel
->setalphatest(max(0, min(1, *cutoff
)));
68 COMMAND(mdlalphatest
, "f");
70 void mdlalphablend(int *blend
)
73 loadingmodel
->setalphablend(*blend
!=0);
76 COMMAND(mdlalphablend
, "i");
78 void mdlglow(int *percent
)
82 if(*percent
>0) glow
= *percent
/100.0f
;
83 else if(*percent
<0) glow
= 0.0f
;
84 loadingmodel
->setglow(glow
);
87 COMMAND(mdlglow
, "i");
89 void mdlenvmap(int *envmapmax
, int *envmapmin
, char *envmap
)
92 loadingmodel
->setenvmap(*envmapmin
, *envmapmax
, envmap
[0] ? cubemapload(envmap
) : NULL
);
95 COMMAND(mdlenvmap
, "iis");
97 void mdltranslucent(float *translucency
)
100 loadingmodel
->settranslucency(*translucency
);
103 COMMAND(mdltranslucent
, "f");
105 void mdlfullbright(float *fullbright
)
108 loadingmodel
->setfullbright(*fullbright
);
111 COMMAND(mdlfullbright
, "f");
113 void mdlshader(char *shader
)
116 loadingmodel
->setshader(lookupshaderbyname(shader
));
119 COMMAND(mdlshader
, "s");
121 void mdlspin(float *rate
)
124 loadingmodel
->spin
= *rate
;
127 COMMAND(mdlspin
, "f");
129 void mdlscale(int *percent
)
133 if(*percent
>0) scale
= *percent
/100.0f
;
134 else if(*percent
<0) scale
= 0.0f
;
135 loadingmodel
->scale
= scale
;
138 COMMAND(mdlscale
, "i");
140 void mdltrans(float *x
, float *y
, float *z
)
143 loadingmodel
->translate
= vec(*x
, *y
, *z
);
146 COMMAND(mdltrans
, "fff");
148 void mdlshadow(int *shadow
)
151 loadingmodel
->shadow
= *shadow
!=0;
154 COMMAND(mdlshadow
, "i");
156 void mdlbb(float *rad
, float *h
, float *eyeheight
)
159 loadingmodel
->collideradius
= *rad
;
160 loadingmodel
->collideheight
= *h
;
161 loadingmodel
->eyeheight
= *eyeheight
;
164 COMMAND(mdlbb
, "fff");
169 result(loadingmodel
->name());
172 COMMAND(mdlname
, "");
176 vector
<mapmodelinfo
> mapmodels
;
178 void mmodel(char *name
, int *tex
)
180 mapmodelinfo
&mmi
= mapmodels
.add();
181 s_strcpy(mmi
.name
, name
);
186 void mapmodelcompat(int *rad
, int *h
, int *tex
, char *name
, char *shadow
)
191 void mapmodelreset() { mapmodels
.setsize(0); }
193 mapmodelinfo
&getmminfo(int i
) { return mapmodels
.inrange(i
) ? mapmodels
[i
] : *(mapmodelinfo
*)0; }
194 const char *mapmodelname(int i
) { return mapmodels
.inrange(i
) ? mapmodels
[i
].name
: NULL
; }
196 COMMAND(mmodel
, "si");
197 COMMANDN(mapmodel
, mapmodelcompat
, "iiiss");
198 COMMAND(mapmodelreset
, "");
202 hashtable
<const char *, model
*> mdllookup
;
204 model
*loadmodel(const char *name
, int i
, bool msg
)
208 if(!mapmodels
.inrange(i
)) return NULL
;
209 mapmodelinfo
&mmi
= mapmodels
[i
];
210 if(mmi
.m
) return mmi
.m
;
213 model
**mm
= mdllookup
.access(name
);
220 s_sprintfd(filename
)("packages/models/%s", name
);
221 show_out_of_renderloop_progress(0, filename
);
238 mdllookup
.access(m
->name(), &m
);
240 if(mapmodels
.inrange(i
) && !mapmodels
[i
].m
) mapmodels
[i
].m
= m
;
246 enumerate(mdllookup
, model
*, m
, delete m
);
249 bool modeloccluded(const vec
¢er
, float radius
)
251 int br
= int(radius
*2)+1;
252 return bboccluded(ivec(int(center
.x
-radius
), int(center
.y
-radius
), int(center
.z
-radius
)), ivec(br
, br
, br
), worldroot
, ivec(0, 0, 0), hdr
.worldsize
/2);
255 VAR(showboundingbox
, 0, 0, 2);
257 void render2dbox(vec
&o
, float x
, float y
, float z
)
259 glBegin(GL_LINE_LOOP
);
260 glVertex3f(o
.x
, o
.y
, o
.z
);
261 glVertex3f(o
.x
, o
.y
, o
.z
+z
);
262 glVertex3f(o
.x
+x
, o
.y
+y
, o
.z
+z
);
263 glVertex3f(o
.x
+x
, o
.y
+y
, o
.z
);
267 void render3dbox(vec
&o
, float tofloor
, float toceil
, float xradius
, float yradius
)
269 if(yradius
<=0) yradius
= xradius
;
271 c
.sub(vec(xradius
, yradius
, tofloor
));
272 float xsz
= xradius
*2, ysz
= yradius
*2;
273 float h
= tofloor
+toceil
;
274 notextureshader
->set();
276 render2dbox(c
, xsz
, 0, h
);
277 render2dbox(c
, 0, ysz
, h
);
278 c
.add(vec(xsz
, ysz
, 0));
279 render2dbox(c
, -xsz
, 0, h
);
280 render2dbox(c
, 0, -ysz
, h
);
284 void renderellipse(vec
&o
, float xradius
, float yradius
, float yaw
)
286 notextureshader
->set();
287 glColor3f(0.5f
, 0.5f
, 0.5f
);
288 glBegin(GL_LINE_LOOP
);
291 vec
p(xradius
*cosf(2*M_PI
*i
/16.0f
), yradius
*sinf(2*M_PI
*i
/16.0f
), 0);
292 p
.rotate_around_z((yaw
+90)*RAD
);
299 void setshadowmatrix(const plane
&p
, const vec
&dir
)
301 float d
= p
.dot(dir
);
304 d
-dir
.x
*p
.x
, -dir
.y
*p
.x
, -dir
.z
*p
.x
, 0,
305 -dir
.x
*p
.y
, d
-dir
.y
*p
.y
, -dir
.z
*p
.y
, 0,
306 -dir
.x
*p
.z
, -dir
.y
*p
.z
, d
-dir
.z
*p
.z
, 0,
307 -dir
.x
*p
.offset
, -dir
.y
*p
.offset
, -dir
.z
*p
.offset
, d
312 VARP(bounddynshadows
, 0, 1, 1);
313 VARP(dynshadow
, 0, 60, 100);
315 void rendershadow(vec
&dir
, model
*m
, int anim
, int varseed
, const vec
&o
, vec center
, float radius
, float yaw
, float pitch
, float speed
, int basetime
, dynent
*d
, int cull
, modelattach
*a
)
318 float dist
= rayfloor(center
, floor
, 0, center
.z
);
319 if(dist
<=0 || dist
>=center
.z
) return;
321 if((cull
&MDL_CULL_VFC
) && refracting
&& center
.z
>=refracting
) return;
322 if(vec(center
).sub(camera1
->o
).dot(floor
)>0) return;
325 if(cull
&MDL_DYNSHADOW
)
327 extern vec shadowdir
;
335 if(!shaddir
.iszero()) shaddir
.normalize();
336 shaddir
.z
= 1.5f
*(dir
.z
*0.5f
+1);
340 glDisable(GL_TEXTURE_2D
);
341 glDepthMask(GL_FALSE
);
343 if(!hasFBO
|| !reflecting
|| hasDS
) glEnable(GL_STENCIL_TEST
);
345 if((!hasFBO
|| !reflecting
|| hasDS
) && bounddynshadows
)
347 nocolorshader
->set();
348 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
349 glStencilFunc(GL_ALWAYS
, 1, 1);
350 glStencilOp(GL_KEEP
, GL_REPLACE
, GL_ZERO
);
355 setshadowmatrix(plane(floor
, -floor
.dot(below
)), shaddir
);
357 loopi(6) if((shaddir
[dimension(i
)]>0)==dimcoord(i
)) loopj(4)
359 const ivec
&cc
= cubecoords
[fv
[i
][j
]];
360 glVertex3f(center
.x
+ (cc
.x
? 1.5f
: -1.5f
)*radius
,
361 center
.y
+ (cc
.y
? 1.5f
: -1.5f
)*radius
,
362 cc
.z
? center
.z
+ dist
+ radius
: below
.z
);
368 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, refracting
&& renderpath
!=R_FIXEDFUNCTION
? GL_FALSE
: GL_TRUE
);
371 float intensity
= dynshadow
/100.0f
;
374 if(renderpath
!=R_FIXEDFUNCTION
) setfogplane(0, max(0.1f
, refracting
-center
.z
));
375 else if(refractfog
) intensity
*= 1 - max(0, min(1, (refracting
- center
.z
)/waterfog
));
377 glColor4f(0, 0, 0, intensity
);
379 static Shader
*dynshadowshader
= NULL
;
380 if(!dynshadowshader
) dynshadowshader
= lookupshaderbyname("dynshadow");
381 dynshadowshader
->set();
383 if(!hasFBO
|| !reflecting
|| hasDS
)
385 glStencilFunc(GL_NOTEQUAL
, bounddynshadows
? 0 : 1, 1);
386 glStencilOp(GL_KEEP
, GL_REPLACE
, GL_REPLACE
);
393 setshadowmatrix(plane(floor
, -floor
.dot(above
)), shaddir
);
394 m
->render(anim
|ANIM_NOSKIN
|ANIM_SHADOW
, varseed
, speed
, basetime
, o
, yaw
, pitch
, d
, a
);
397 glEnable(GL_TEXTURE_2D
);
398 glDepthMask(GL_TRUE
);
400 if(!hasFBO
|| !reflecting
|| hasDS
) glDisable(GL_STENCIL_TEST
);
406 int anim
, varseed
, tex
;
407 float yaw
, pitch
, speed
;
416 vector
<batchedmodel
> batched
;
418 static vector
<modelbatch
*> batches
;
419 static vector
<modelattach
> modelattached
;
420 static int numbatches
= -1;
421 static occludequery
*modelquery
= NULL
;
423 void startmodelbatches()
426 modelattached
.setsizenodelete(0);
429 batchedmodel
&addbatchedmodel(model
*m
)
431 modelbatch
*b
= NULL
;
432 if(m
->batch
>=0 && m
->batch
<numbatches
&& batches
[m
->batch
]->m
==m
) b
= batches
[m
->batch
];
435 if(numbatches
<batches
.length())
437 b
= batches
[numbatches
];
438 b
->batched
.setsizenodelete(0);
440 else b
= batches
.add(new modelbatch
);
442 m
->batch
= numbatches
++;
444 batchedmodel
&bm
= b
->batched
.add();
445 bm
.query
= modelquery
;
449 void renderbatchedmodel(model
*m
, batchedmodel
&b
)
451 modelattach
*a
= NULL
;
452 if(b
.attached
>=0) a
= &modelattached
[b
.attached
];
453 if((!shadowmap
|| renderpath
==R_FIXEDFUNCTION
) && (b
.cull
&(MDL_SHADOW
|MDL_DYNSHADOW
)) && dynshadow
&& hasstencil
&& (!reflecting
|| refracting
))
456 float radius
= m
->boundsphere(0/*frame*/, center
, a
); // FIXME
458 rendershadow(b
.dir
, m
, b
.anim
, b
.varseed
, b
.pos
, center
, radius
, b
.yaw
, b
.pitch
, b
.speed
, b
.basetime
, b
.d
, b
.cull
, a
);
459 if((b
.cull
&MDL_CULL_VFC
) && refracting
&& center
.z
-radius
>=refracting
) return;
463 if(shadowmapping
) anim
|= ANIM_NOSKIN
;
464 else if(b
.cull
&MDL_TRANSLUCENT
) anim
|= ANIM_TRANSLUCENT
;
467 m
->render(anim
, b
.varseed
, b
.speed
, b
.basetime
, b
.pos
, b
.yaw
, b
.pitch
, b
.d
, a
, b
.color
, b
.dir
);
470 struct translucentmodel
473 batchedmodel
*batched
;
477 static int sorttranslucentmodels(const translucentmodel
*x
, const translucentmodel
*y
)
479 if(x
->dist
> y
->dist
) return -1;
480 if(x
->dist
< y
->dist
) return 1;
484 void endmodelbatches()
486 vector
<translucentmodel
> translucent
;
489 modelbatch
&b
= *batches
[i
];
490 if(b
.batched
.empty()) continue;
491 bool rendered
= false;
492 occludequery
*query
= NULL
;
495 batchedmodel
&bm
= b
.batched
[j
];
498 if(query
) endquery(query
);
500 if(query
) startquery(query
);
502 if(bm
.cull
&MDL_TRANSLUCENT
&& (!query
|| query
->owner
==bm
.d
))
504 translucentmodel
&tm
= translucent
.add();
507 tm
.dist
= camera1
->o
.dist(bm
.pos
);
510 if(!rendered
) { b
.m
->startrender(); rendered
= true; }
511 renderbatchedmodel(b
.m
, bm
);
513 if(query
) endquery(query
);
514 if(rendered
) b
.m
->endrender();
516 if(translucent
.length())
518 translucent
.sort(sorttranslucentmodels
);
519 model
*lastmodel
= NULL
;
520 occludequery
*query
= NULL
;
523 translucentmodel
&tm
= translucent
[i
];
526 if(lastmodel
) lastmodel
->endrender();
527 (lastmodel
= tm
.m
)->startrender();
529 if(query
!=tm
.batched
->query
)
531 if(query
) endquery(query
);
532 query
= tm
.batched
->query
;
533 if(query
) startquery(query
);
535 renderbatchedmodel(tm
.m
, *tm
.batched
);
537 if(query
) endquery(query
);
538 if(lastmodel
) lastmodel
->endrender();
543 void startmodelquery(occludequery
*query
)
550 int querybatches
= 0;
553 modelbatch
&b
= *batches
[i
];
554 if(b
.batched
.empty() || b
.batched
.last().query
!=modelquery
) continue;
559 if(!querybatches
) modelquery
->fragments
= 0;
563 int minattached
= modelattached
.length();
564 startquery(modelquery
);
567 modelbatch
&b
= *batches
[i
];
568 if(b
.batched
.empty() || b
.batched
.last().query
!=modelquery
) continue;
572 batchedmodel
&bm
= b
.batched
.pop();
573 if(bm
.attached
>=0) minattached
= min(minattached
, bm
.attached
);
574 renderbatchedmodel(b
.m
, bm
);
576 while(b
.batched
.length() && b
.batched
.last().query
==modelquery
);
579 endquery(modelquery
);
581 modelattached
.setsizenodelete(minattached
);
584 VARP(maxmodelradiusdistance
, 10, 100, 1000);
586 void rendermodelquery(model
*m
, dynent
*d
, const vec
¢er
, float radius
)
588 d
->query
= newquery(d
);
589 if(!d
->query
) return;
590 nocolorshader
->set();
591 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
592 glDepthMask(GL_FALSE
);
593 startquery(d
->query
);
594 int br
= int(radius
*2)+1;
595 drawbb(ivec(int(center
.x
-radius
), int(center
.y
-radius
), int(center
.z
-radius
)), ivec(br
, br
, br
));
597 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, refracting
&& renderpath
!=R_FIXEDFUNCTION
? GL_FALSE
: GL_TRUE
);
598 glDepthMask(GL_TRUE
);
601 void rendermodel(vec
&color
, vec
&dir
, const char *mdl
, int anim
, int varseed
, int tex
, const vec
&o
, float yaw
, float pitch
, float speed
, int basetime
, dynent
*d
, int cull
, modelattach
*a
)
603 if(shadowmapping
&& !(cull
&(MDL_SHADOW
|MDL_DYNSHADOW
))) return;
604 model
*m
= loadmodel(mdl
);
608 bool shadow
= (!shadowmap
|| renderpath
==R_FIXEDFUNCTION
) && (cull
&(MDL_SHADOW
|MDL_DYNSHADOW
)) && dynshadow
&& hasstencil
;
611 radius
= m
->boundsphere(0/*frame*/, center
, a
); // FIXME
613 if(cull
&MDL_CULL_DIST
&& center
.dist(camera1
->o
)/radius
>maxmodelradiusdistance
) return;
614 if(cull
&MDL_CULL_VFC
)
620 if(center
.z
+radius
<refracting
-waterfog
|| (!shadow
&& center
.z
-radius
>=refracting
)) return;
622 else if(center
.z
+radius
<=reflecting
) return;
623 if(center
.dist(camera1
->o
)-radius
>reflectdist
) return;
625 if(isvisiblesphere(radius
, center
) >= VFC_FOGGED
) return;
626 if(shadowmapping
&& !isshadowmapcaster(center
, radius
)) return;
632 if(cull
&MDL_CULL_OCCLUDED
&& d
->occluded
>=OCCLUDE_PARENT
) return;
633 if(cull
&MDL_CULL_QUERY
&& hasOQ
&& oqdynent
&& d
->occluded
+1>=OCCLUDE_BB
&& d
->query
&& d
->query
->owner
==d
&& checkquery(d
->query
)) return;
635 if(!addshadowmapcaster(center
, radius
, radius
)) return;
637 else if(cull
&MDL_CULL_OCCLUDED
&& modeloccluded(center
, radius
))
639 if(!reflecting
&& !refracting
&& d
)
641 d
->occluded
= OCCLUDE_PARENT
;
642 if(cull
&MDL_CULL_QUERY
&& hasOQ
&& oqdynent
) rendermodelquery(m
, d
, center
, radius
);
646 else if(cull
&MDL_CULL_QUERY
&& hasOQ
&& oqdynent
&& d
&& d
->query
&& d
->query
->owner
==d
&& checkquery(d
->query
))
648 if(!reflecting
&& !refracting
)
650 if(d
->occluded
<OCCLUDE_BB
) d
->occluded
++;
651 rendermodelquery(m
, d
, center
, radius
);
656 if(showboundingbox
&& !shadowmapping
)
658 if(d
&& showboundingbox
==1)
660 render3dbox(d
->o
, d
->eyeheight
, d
->aboveeye
, d
->radius
);
661 renderellipse(d
->o
, d
->xradius
, d
->yradius
, d
->yaw
);
666 if(showboundingbox
==1) m
->collisionbox(0, center
, radius
);
667 else m
->boundbox(0, center
, radius
);
668 rotatebb(center
, radius
, int(yaw
));
670 render3dbox(center
, radius
.z
, radius
.z
, radius
.x
, radius
.y
);
674 if(d
&& !shadowmapping
)
676 if(!reflecting
&& !refracting
) d
->occluded
= OCCLUDE_NOTHING
;
677 lightreaching(d
->o
, color
, dir
);
678 cl
->lighteffects(d
, color
, dir
);
680 vec
dyncolor(color
), dyndir(dir
);
681 if(!shadowmapping
) dynlightreaching(o
, dyncolor
, dyndir
);
682 if(a
) for(int i
= 0; a
[i
].name
; i
++)
684 a
[i
].m
= loadmodel(a
[i
].name
);
685 if(a
[i
].m
&& a
[i
].m
->type()!=m
->type()) a
[i
].m
= NULL
;
688 bool doOQ
= cull
&MDL_CULL_QUERY
&& !reflecting
&& !refracting
&& !shadowmapping
&& hasOQ
&& oqdynent
&& d
;
692 batchedmodel
&b
= addbatchedmodel(m
);
702 b
.basetime
= basetime
;
705 b
.attached
= a
? modelattached
.length() : -1;
706 if(a
) for(int i
= 0;; i
++) { modelattached
.add(a
[i
]); if(!a
[i
].name
) break; }
707 if(doOQ
) d
->query
= b
.query
= newquery(d
);
713 if(shadow
&& (!reflecting
|| refracting
))
715 rendershadow(dyndir
, m
, anim
, varseed
, o
, center
, radius
, yaw
, pitch
, speed
, basetime
, d
, cull
, a
);
716 if((cull
&MDL_CULL_VFC
) && refracting
&& center
.z
-radius
>=refracting
) { m
->endrender(); return; }
719 if(shadowmapping
) anim
|= ANIM_NOSKIN
;
720 else if(cull
&MDL_TRANSLUCENT
) anim
|= ANIM_TRANSLUCENT
;
724 d
->query
= newquery(d
);
725 if(d
->query
) startquery(d
->query
);
729 m
->render(anim
, varseed
, speed
, basetime
, o
, yaw
, pitch
, d
, a
, dyncolor
, dyndir
);
731 if(doOQ
&& d
->query
) endquery(d
->query
);
736 void abovemodel(vec
&o
, const char *mdl
)
738 model
*m
= loadmodel(mdl
);
740 o
.z
+= m
->above(0/*frame*/);
743 bool matchanim(const char *name
, const char *pattern
)
747 const char *s
= name
;
752 if(!c
|| c
=='|') break;
755 if(!*s
|| isspace(*s
)) break;
756 do s
++; while(*s
&& !isspace(*s
));
758 else if(c
!=*s
) break;
761 if(!*s
&& (!c
|| c
=='|')) return true;
762 pattern
= strchr(pattern
, '|');
768 void findanims(const char *pattern
, vector
<int> &anims
)
770 static const char *names
[] =
772 "dead", "dying", "idle",
773 "forward", "backward", "left", "right",
774 "punch", "shoot", "pain",
775 "jump", "sink", "swim",
776 "edit", "lag", "taunt", "win", "lose",
777 "gun shoot", "gun idle",
778 "vwep", "shield", "powerup",
779 "mapmodel", "trigger"
781 loopi(sizeof(names
)/sizeof(names
[0])) if(matchanim(names
[i
], pattern
)) anims
.add(i
);
784 void loadskin(const char *dir
, const char *altdir
, Texture
*&skin
, Texture
*&masks
) // model skin sharing
786 #define ifnoload(tex, path) if((tex = textureload(path, 0, true, false))==notexture)
787 #define tryload(tex, path, prefix, name) \
788 s_sprintfd(path)("%spackages/models/%s/%s.jpg", prefix, dir, name); \
789 ifnoload(tex, path) \
791 strcpy(path+strlen(path)-3, "png"); \
792 ifnoload(tex, path) \
794 s_sprintf(path)("%spackages/models/%s/%s.jpg", prefix, altdir, name); \
795 ifnoload(tex, path) \
797 strcpy(path+strlen(path)-3, "png"); \
798 ifnoload(tex, path) return; \
804 tryload(skin
, skinpath
, "", "skin");
805 if(renderpath
!=R_FIXEDFUNCTION
) { tryload(masks
, maskspath
, "", "masks"); }
806 else { tryload(masks
, maskspath
, "<ffmask:25>", "masks"); }
809 // convenient function that covers the usual anims for players/monsters/npcs
811 VAR(animoverride
, 0, 0, NUMANIMS
-1);
812 VAR(testanims
, 0, 0, 1);
814 void interpolateorientation(dynent
*d
, float &interpyaw
, float &interppitch
)
816 if(!orientinterpolationtime
) { interpyaw
= d
->yaw
; interppitch
= d
->pitch
; return; }
817 if(d
->orientmillis
!=lastmillis
)
819 float yaw
= d
->yaw
, pitch
= d
->pitch
;
820 if(yaw
-d
->lastyaw
>=180) yaw
-= 360;
821 else if(d
->lastyaw
-yaw
>=180) yaw
+= 360;
822 d
->lastyaw
+= (yaw
-d
->lastyaw
)*min(orientinterpolationtime
, lastmillis
-d
->orientmillis
)/float(orientinterpolationtime
);
823 d
->lastpitch
+= (pitch
-d
->lastpitch
)*min(orientinterpolationtime
, lastmillis
-d
->orientmillis
)/float(orientinterpolationtime
);
824 d
->orientmillis
= lastmillis
;
826 interpyaw
= d
->lastyaw
;
827 interppitch
= d
->lastpitch
;
830 void renderclient(dynent
*d
, const char *mdlname
, modelattach
*attachments
, int attack
, int attackdelay
, int lastaction
, int lastpain
, float sink
)
832 int anim
= ANIM_IDLE
|ANIM_LOOP
;
833 float yaw
= d
->yaw
, pitch
= d
->pitch
;
834 if(d
->type
==ENT_PLAYER
&& d
!=player
&& orientinterpolationtime
) interpolateorientation(d
, yaw
, pitch
);
836 o
.z
-= d
->eyeheight
+ sink
;
837 int varseed
= (int)(size_t)d
, basetime
= 0;
838 if(animoverride
) anim
= animoverride
|ANIM_LOOP
;
839 else if(d
->state
==CS_DEAD
)
845 int t
= lastmillis
-lastpain
;
846 if(t
<0 || t
>20000) return;
847 if(t
>500) { anim
= ANIM_DEAD
|ANIM_LOOP
; if(t
>1600) { t
-= 1600; o
.z
-= t
*t
/10000000000.0f
*t
/16.0f
; } }
848 if(o
.z
<-1000) return;
850 else if(d
->state
==CS_EDITING
|| d
->state
==CS_SPECTATOR
) anim
= ANIM_EDIT
|ANIM_LOOP
;
851 else if(d
->state
==CS_LAGGED
) anim
= ANIM_LAG
|ANIM_LOOP
;
854 if(lastmillis
-lastpain
<300)
860 else if(attack
<0 || (d
->type
!=ENT_AI
&& lastmillis
-lastaction
<attackdelay
))
862 anim
= attack
<0 ? -attack
: attack
;
863 basetime
= lastaction
;
864 varseed
+= lastaction
;
867 if(d
->inwater
&& d
->physstate
<=PHYS_FALL
) anim
|= (((cl
->allowmove(d
) && (d
->move
|| d
->strafe
)) || d
->vel
.z
+d
->gravity
.z
>0 ? ANIM_SWIM
: ANIM_SINK
)|ANIM_LOOP
)<<ANIM_SECONDARY
;
868 else if(d
->timeinair
>100) anim
|= (ANIM_JUMP
|ANIM_END
)<<ANIM_SECONDARY
;
869 else if(cl
->allowmove(d
))
871 if(d
->move
>0) anim
|= (ANIM_FORWARD
|ANIM_LOOP
)<<ANIM_SECONDARY
;
872 else if(d
->strafe
) anim
|= ((d
->strafe
>0 ? ANIM_LEFT
: ANIM_RIGHT
)|ANIM_LOOP
)<<ANIM_SECONDARY
;
873 else if(d
->move
<0) anim
|= (ANIM_BACKWARD
|ANIM_LOOP
)<<ANIM_SECONDARY
;
876 if((anim
&ANIM_INDEX
)==ANIM_IDLE
&& (anim
>>ANIM_SECONDARY
)&ANIM_INDEX
) anim
>>= ANIM_SECONDARY
;
878 if(!((anim
>>ANIM_SECONDARY
)&ANIM_INDEX
)) anim
|= (ANIM_IDLE
|ANIM_LOOP
)<<ANIM_SECONDARY
;
879 int flags
= MDL_CULL_VFC
| MDL_CULL_OCCLUDED
| MDL_CULL_QUERY
;
880 if(d
->type
!=ENT_PLAYER
) flags
|= MDL_CULL_DIST
;
881 if((anim
&ANIM_INDEX
)!=ANIM_DEAD
) flags
|= MDL_DYNSHADOW
;
882 if(d
->state
==CS_LAGGED
) flags
|= MDL_TRANSLUCENT
;
884 rendermodel(color
, dir
, mdlname
, anim
, varseed
, 0, o
, testanims
&& d
==player
? 0 : yaw
+90, pitch
, 0, basetime
, d
, flags
, attachments
);
887 void setbbfrommodel(dynent
*d
, const char *mdl
)
889 model
*m
= loadmodel(mdl
);
892 m
->collisionbox(0, center
, radius
);
893 if(d
->type
==ENT_INANIMATE
&& !m
->ellipsecollide
)
895 d
->collidetype
= COLLIDE_AABB
;
896 rotatebb(center
, radius
, int(d
->yaw
));
898 d
->xradius
= radius
.x
+ fabs(center
.x
);
899 d
->yradius
= radius
.y
+ fabs(center
.y
);
900 d
->radius
= max(d
->xradius
, d
->yradius
);
901 if(d
->collidetype
!=COLLIDE_ELLIPSE
) d
->xradius
= d
->yradius
= d
->radius
;
902 d
->eyeheight
= (center
.z
-radius
.z
) + radius
.z
*2*m
->eyeheight
;
903 d
->aboveeye
= radius
.z
*2*(1.0f
-m
->eyeheight
);