From f52bf7e25ebeb8200091f0e85b09683e08e17c5f Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Sat, 13 Sep 2008 02:24:48 +0000 Subject: [PATCH] stencil shadows git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2551 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- .../models/mapmodels/barrel-toxic/md3.cfg | 2 +- packages/models/mapmodels/barrel/md2.cfg | 2 + packages/models/mapmodels/barrel2/md2.cfg | 2 + packages/models/mapmodels/bench/md2.cfg | 2 + packages/models/mapmodels/can/md2.cfg | 2 + packages/models/mapmodels/can2/md2.cfg | 2 + packages/models/mapmodels/dumpster/md2.cfg | 2 + packages/models/mapmodels/europalette/md2.cfg | 2 + packages/models/mapmodels/locker/md2.cfg | 2 + .../models/mapmodels/toca/trashbag/md2.cfg | 2 + .../models/mapmodels/toca/trashcan/md3.cfg | 2 + packages/models/mapmodels/toca/tyre/md2.cfg | 2 + packages/models/mapmodels/toca/wheel/md2.cfg | 2 + packages/models/misc/gib01/md2.cfg | 4 +- packages/models/misc/gib02/md2.cfg | 4 +- packages/models/misc/gib03/md2.cfg | 4 +- packages/models/pickups/md2.cfg | 2 + packages/models/playermodels/md2.cfg | 3 + packages/models/weapons/assault/world/md2.cfg | 1 + .../models/weapons/grenade/static/md2.cfg | 2 + packages/models/weapons/grenade/world/md2.cfg | 1 + packages/models/weapons/knife/world/md2.cfg | 1 + packages/models/weapons/pistol/world/md2.cfg | 1 + packages/models/weapons/shotgun/world/md2.cfg | 1 + packages/models/weapons/sniper/world/md2.cfg | 1 + packages/models/weapons/subgun/world/md2.cfg | 1 + source/src/cube.h | 1 + source/src/entities.cpp | 11 +- source/src/geom.h | 11 + source/src/main.cpp | 24 +- source/src/md2.h | 12 +- source/src/md3.h | 25 +- source/src/menus.cpp | 2 +- source/src/model.h | 7 +- source/src/modelcache.h | 154 +++++++++ source/src/protos.h | 8 +- source/src/rendergl.cpp | 128 +++++++- source/src/rendermodel.cpp | 52 ++- source/src/tools.h | 1 + source/src/vertmodel.h | 296 ++++++++++++++++-- source/src/weapon.cpp | 4 +- 41 files changed, 711 insertions(+), 77 deletions(-) create mode 100644 packages/models/mapmodels/barrel/md2.cfg create mode 100644 packages/models/mapmodels/barrel2/md2.cfg create mode 100644 packages/models/mapmodels/bench/md2.cfg create mode 100644 packages/models/mapmodels/can/md2.cfg create mode 100644 packages/models/mapmodels/can2/md2.cfg create mode 100644 packages/models/mapmodels/dumpster/md2.cfg create mode 100644 packages/models/mapmodels/europalette/md2.cfg create mode 100644 packages/models/mapmodels/locker/md2.cfg create mode 100644 packages/models/mapmodels/toca/trashbag/md2.cfg create mode 100644 packages/models/mapmodels/toca/trashcan/md3.cfg create mode 100644 packages/models/mapmodels/toca/tyre/md2.cfg create mode 100644 packages/models/mapmodels/toca/wheel/md2.cfg create mode 100644 packages/models/pickups/md2.cfg create mode 100644 source/src/modelcache.h diff --git a/packages/models/mapmodels/barrel-toxic/md3.cfg b/packages/models/mapmodels/barrel-toxic/md3.cfg index eb80814d..6da2321e 100644 --- a/packages/models/mapmodels/barrel-toxic/md3.cfg +++ b/packages/models/mapmodels/barrel-toxic/md3.cfg @@ -1,3 +1,3 @@ md3load tris.md3 md3skin main skin.jpg -md3skin toxic skin_toxic.jpg \ No newline at end of file +md3skin toxic skin_toxic.jpg diff --git a/packages/models/mapmodels/barrel/md2.cfg b/packages/models/mapmodels/barrel/md2.cfg new file mode 100644 index 00000000..54fa9294 --- /dev/null +++ b/packages/models/mapmodels/barrel/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 12 + diff --git a/packages/models/mapmodels/barrel2/md2.cfg b/packages/models/mapmodels/barrel2/md2.cfg new file mode 100644 index 00000000..54fa9294 --- /dev/null +++ b/packages/models/mapmodels/barrel2/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 12 + diff --git a/packages/models/mapmodels/bench/md2.cfg b/packages/models/mapmodels/bench/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/bench/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/mapmodels/can/md2.cfg b/packages/models/mapmodels/can/md2.cfg new file mode 100644 index 00000000..559bcd1d --- /dev/null +++ b/packages/models/mapmodels/can/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 2 + diff --git a/packages/models/mapmodels/can2/md2.cfg b/packages/models/mapmodels/can2/md2.cfg new file mode 100644 index 00000000..559bcd1d --- /dev/null +++ b/packages/models/mapmodels/can2/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 2 + diff --git a/packages/models/mapmodels/dumpster/md2.cfg b/packages/models/mapmodels/dumpster/md2.cfg new file mode 100644 index 00000000..b9459fe9 --- /dev/null +++ b/packages/models/mapmodels/dumpster/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 16 + diff --git a/packages/models/mapmodels/europalette/md2.cfg b/packages/models/mapmodels/europalette/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/europalette/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/mapmodels/locker/md2.cfg b/packages/models/mapmodels/locker/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/locker/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/mapmodels/toca/trashbag/md2.cfg b/packages/models/mapmodels/toca/trashbag/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/toca/trashbag/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/mapmodels/toca/trashcan/md3.cfg b/packages/models/mapmodels/toca/trashcan/md3.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/toca/trashcan/md3.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/mapmodels/toca/tyre/md2.cfg b/packages/models/mapmodels/toca/tyre/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/toca/tyre/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/mapmodels/toca/wheel/md2.cfg b/packages/models/mapmodels/toca/wheel/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/mapmodels/toca/wheel/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/misc/gib01/md2.cfg b/packages/models/misc/gib01/md2.cfg index 86b2aade..ad447fba 100644 --- a/packages/models/misc/gib01/md2.cfg +++ b/packages/models/misc/gib01/md2.cfg @@ -1 +1,3 @@ -mdlscale 200 \ No newline at end of file +mdlscale 200 +mdlshadowdist 8 + diff --git a/packages/models/misc/gib02/md2.cfg b/packages/models/misc/gib02/md2.cfg index 86b2aade..ad447fba 100644 --- a/packages/models/misc/gib02/md2.cfg +++ b/packages/models/misc/gib02/md2.cfg @@ -1 +1,3 @@ -mdlscale 200 \ No newline at end of file +mdlscale 200 +mdlshadowdist 8 + diff --git a/packages/models/misc/gib03/md2.cfg b/packages/models/misc/gib03/md2.cfg index 86b2aade..ad447fba 100644 --- a/packages/models/misc/gib03/md2.cfg +++ b/packages/models/misc/gib03/md2.cfg @@ -1 +1,3 @@ -mdlscale 200 \ No newline at end of file +mdlscale 200 +mdlshadowdist 8 + diff --git a/packages/models/pickups/md2.cfg b/packages/models/pickups/md2.cfg new file mode 100644 index 00000000..e5784a3c --- /dev/null +++ b/packages/models/pickups/md2.cfg @@ -0,0 +1,2 @@ +mdlshadowdist 8 + diff --git a/packages/models/playermodels/md2.cfg b/packages/models/playermodels/md2.cfg index f2d16e2d..072f2bf8 100644 --- a/packages/models/playermodels/md2.cfg +++ b/packages/models/playermodels/md2.cfg @@ -1,2 +1,5 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 +mdlcachelimit 16 + diff --git a/packages/models/weapons/assault/world/md2.cfg b/packages/models/weapons/assault/world/md2.cfg index 0b51ec49..5cb7297e 100644 --- a/packages/models/weapons/assault/world/md2.cfg +++ b/packages/models/weapons/assault/world/md2.cfg @@ -1,5 +1,6 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 md2tag tag_muzzle -Y md2emit tag_muzzle 11 50 75 diff --git a/packages/models/weapons/grenade/static/md2.cfg b/packages/models/weapons/grenade/static/md2.cfg index a1e220bc..ad447fba 100644 --- a/packages/models/weapons/grenade/static/md2.cfg +++ b/packages/models/weapons/grenade/static/md2.cfg @@ -1 +1,3 @@ mdlscale 200 +mdlshadowdist 8 + diff --git a/packages/models/weapons/grenade/world/md2.cfg b/packages/models/weapons/grenade/world/md2.cfg index e7d478a7..24559ded 100644 --- a/packages/models/weapons/grenade/world/md2.cfg +++ b/packages/models/weapons/grenade/world/md2.cfg @@ -1,3 +1,4 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 diff --git a/packages/models/weapons/knife/world/md2.cfg b/packages/models/weapons/knife/world/md2.cfg index e7d478a7..24559ded 100644 --- a/packages/models/weapons/knife/world/md2.cfg +++ b/packages/models/weapons/knife/world/md2.cfg @@ -1,3 +1,4 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 diff --git a/packages/models/weapons/pistol/world/md2.cfg b/packages/models/weapons/pistol/world/md2.cfg index 71608cb2..f6c3009a 100644 --- a/packages/models/weapons/pistol/world/md2.cfg +++ b/packages/models/weapons/pistol/world/md2.cfg @@ -1,5 +1,6 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 md2tag tag_muzzle -Y md2emit tag_muzzle 11 50 25 diff --git a/packages/models/weapons/shotgun/world/md2.cfg b/packages/models/weapons/shotgun/world/md2.cfg index 0b51ec49..5cb7297e 100644 --- a/packages/models/weapons/shotgun/world/md2.cfg +++ b/packages/models/weapons/shotgun/world/md2.cfg @@ -1,5 +1,6 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 md2tag tag_muzzle -Y md2emit tag_muzzle 11 50 75 diff --git a/packages/models/weapons/sniper/world/md2.cfg b/packages/models/weapons/sniper/world/md2.cfg index 78805176..779156cd 100644 --- a/packages/models/weapons/sniper/world/md2.cfg +++ b/packages/models/weapons/sniper/world/md2.cfg @@ -1,5 +1,6 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 md2tag tag_muzzle -Y md2emit tag_muzzle 11 50 diff --git a/packages/models/weapons/subgun/world/md2.cfg b/packages/models/weapons/subgun/world/md2.cfg index cddd06dc..df6a4489 100644 --- a/packages/models/weapons/subgun/world/md2.cfg +++ b/packages/models/weapons/subgun/world/md2.cfg @@ -1,5 +1,6 @@ mdlscale 155 mdltrans 0 0 24 +mdlshadowdist 32 md2tag tag_muzzle -Y md2emit tag_muzzle 11 50 55 diff --git a/source/src/cube.h b/source/src/cube.h index 76b6d998..fd288256 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -39,6 +39,7 @@ extern int gamemode, nextmode; extern int gamespeed; extern int xtraverts; extern bool minimap, reflecting, refracting; +extern int stenciling, stencilshadow; extern bool intermission; extern int maxclients; extern hashtable mapinfo; diff --git a/source/src/entities.cpp b/source/src/entities.cpp index 9c04691b..00c7e5b6 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -21,9 +21,12 @@ const char *entmdlnames[] = "pickups/pistolclips", "pickups/ammobox", "pickups/nades", "pickups/health", "pickups/kevlar", "pickups/akimbo", }; -void renderent(entity &e, const char *mdlname, float z, float yaw, int anim = ANIM_MAPMODEL|ANIM_LOOP, int basetime = 0, float speed = 0) +void renderent(entity &e) { - rendermodel(mdlname, anim, 0, 0, vec(e.x, e.y, z+S(e.x, e.y)->floor), yaw, 0, speed, basetime); + const char *mdlname = entmdlnames[e.type-I_CLIPS]; + float z = (float)(1+sinf(lastmillis/100.0f+e.x+e.y)/20), + yaw = lastmillis/10.0f; + rendermodel(mdlname, ANIM_MAPMODEL|ANIM_LOOP|ANIM_DYNALLOC, 0, 0, vec(e.x, e.y, z+S(e.x, e.y)->floor), yaw, 0); } void renderentities() @@ -51,7 +54,7 @@ void renderentities() { if((!OUTBORD(e.x, e.y) && e.spawned) | editmode) { - renderent(e, entmdlnames[e.type-I_CLIPS], (float)(1+sinf(lastmillis/100.0f+e.x+e.y)/20), lastmillis/10.0f); + renderent(e); } } } @@ -64,7 +67,7 @@ void renderentities() if(f.actor && f.actor != player1) { s_sprintfd(path)("pickups/flags/small_%s%s", m_ktf || m_tktf ? "" : team_string(i), m_htf ? "_htf" : m_ktf || m_tktf ? "ktf" : ""); - rendermodel(path, ANIM_FLAG|ANIM_START, 0, 0, vec(f.actor->o).add(vec(0, 0, 0.3f+(sinf(lastmillis/100.0f)+1)/10)), lastmillis/2.5f, 0, 120.0f); + rendermodel(path, ANIM_FLAG|ANIM_START|ANIM_DYNALLOC, 0, 0, vec(f.actor->o).add(vec(0, 0, 0.3f+(sinf(lastmillis/100.0f)+1)/10)), lastmillis/2.5f, 0, 120.0f); } break; case CTFF_INBASE: diff --git a/source/src/geom.h b/source/src/geom.h index 36ec9112..61cd31bc 100644 --- a/source/src/geom.h +++ b/source/src/geom.h @@ -4,6 +4,7 @@ struct vec { struct { float x, y, z; }; float v[3]; + int i[3]; }; vec() {} @@ -70,6 +71,16 @@ struct vec } }; +static inline bool htcmp(const vec &x, const vec &y) +{ + return x == y; +} + +static inline uint hthash(const vec &k) +{ + return k.i[0]^k.i[1]^k.i[2]; +} + struct vec4 { union diff --git a/source/src/main.cpp b/source/src/main.cpp index 82baed94..c9c67566 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -83,6 +83,7 @@ VARF(scr_w, 320, 1024, 10000, initwarning("screen resolution")); VARF(scr_h, 200, 768, 10000, initwarning("screen resolution")); VARF(colorbits, 0, 0, 32, initwarning("color depth")); VARF(depthbits, 0, 0, 32, initwarning("depth-buffer precision")); +VARF(stencilbits, 0, 8, 32, initwarning("stencil-buffer precision")); VARF(fsaa, -1, -1, 16, initwarning("anti-aliasing")); VARF(vsync, -1, -1, 1, initwarning("vertical sync")); @@ -118,6 +119,7 @@ void writeinitcfg() fprintf(f, "scr_h %d\n", scr_h); fprintf(f, "colorbits %d\n", colorbits); fprintf(f, "depthbits %d\n", depthbits); + fprintf(f, "stencilbits %d\n", stencilbits); fprintf(f, "fsaa %d\n", fsaa); fprintf(f, "vsync %d\n", vsync); extern int fullscreen, audio, soundchannels; @@ -243,11 +245,13 @@ void setupscreen(int &usedcolorbits, int &useddepthbits, int &usedfsaa) #endif static int configs[] = { - 0x3, /* try everything */ - 0x2, 0x1, /* try disabling one at a time */ + 0x7, /* try everything */ + 0x6, 0x5, 0x3, /* try disabling one at a time */ + 0x4, 0x2, 0x1, /* try disabling two at a time */ 0 /* try disabling everything */ }; int config = 0; + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); if(!depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); if(!fsaa) { @@ -258,12 +262,19 @@ void setupscreen(int &usedcolorbits, int &useddepthbits, int &usedfsaa) { config = configs[i]; if(!depthbits && config&1) continue; - if(fsaa<=0 && config&2) continue; + if(!stencilbits && config&2) continue; + if(fsaa<=0 && config&4) continue; if(depthbits) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config&1 ? depthbits : 16); + if(stencilbits) + { + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config&2 ? stencilbits : 0); + hasstencil = (config&2)!=0; + } + else hasstencil = false; if(fsaa>0) { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&2 ? 1 : 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&2 ? fsaa : 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, config&4 ? 1 : 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config&4 ? fsaa : 0); } screen = SDL_SetVideoMode(scr_w, scr_h, hasbpp ? colorbits : 0, SDL_OPENGL|flags); if(screen) break; @@ -273,7 +284,8 @@ void setupscreen(int &usedcolorbits, int &useddepthbits, int &usedfsaa) { if(!hasbpp) conoutf("%d bit color buffer not supported - disabling", colorbits); if(depthbits && (config&1)==0) conoutf("%d bit z-buffer not supported - disabling", depthbits); - if(fsaa>0 && (config&2)==0) conoutf("%dx anti-aliasing not supported - disabling", fsaa); + if(stencilbits && (config&2)==0) conoutf("%d bit stencil buffer not supported - disabling", stencilbits); + if(fsaa>0 && (config&4)==0) conoutf("%dx anti-aliasing not supported - disabling", fsaa); } scr_w = screen->w; diff --git a/source/src/md2.h b/source/src/md2.h index aecdd726..c42ddc6b 100644 --- a/source/src/md2.h +++ b/source/src/md2.h @@ -119,7 +119,7 @@ struct md2 : vertmodel m.tris = new tri[m.numtris]; memcpy(m.tris, trigen.getbuf(), m.numtris*sizeof(tri)); - m.verts = new vec[m.numverts*numframes]; + m.verts = new vec[m.numverts*numframes+1]; md2_vertex *tmpverts = new md2_vertex[header.numvertices]; int frame_offset = header.offsetframes; @@ -182,11 +182,18 @@ struct md2 : vertmodel if(!cullface) glDisable(GL_CULL_FACE); else if(anim&ANIM_MIRROR) glCullFace(GL_BACK); + if(stenciling && !parts[0]->index) + { + shadowdir = vec(0, 1/SQRT2, -1/SQRT2); + shadowdir.rotate_around_z((-shadowyaw-yaw-180.0f)*RAD); + (shadowpos = shadowdir).mul(shadowdist); + } + glPushMatrix(); glTranslatef(o.x, o.y, o.z); glRotatef(yaw+180, 0, 0, 1); glRotatef(pitch, 0, -1, 0); - if(anim&ANIM_MIRROR || scale!=1) glScalef(anim&ANIM_MIRROR ? -scale : scale, scale, scale); + if(anim&ANIM_MIRROR || scale!=1) glScalef(scale, anim&ANIM_MIRROR ? -scale : scale, scale); parts[0]->render(anim, varseed, speed, basetime, d); glPopMatrix(); @@ -245,6 +252,7 @@ struct md2 : vertmodel loadingmd2 = 0; loopv(parts) parts[i]->scaleverts(scale/16.0f, vec(translate.x, -translate.y, translate.z)); radius = calcradius(); + if(shadowdist) calcneighbors(); return loaded = true; } }; diff --git a/source/src/md3.h b/source/src/md3.h index b48bf572..93839128 100644 --- a/source/src/md3.h +++ b/source/src/md3.h @@ -79,12 +79,12 @@ struct md3 : vertmodel fread(&tag, sizeof(md3tag), 1, f); endianswap(&tag.pos, sizeof(float), 12); if(tag.name[0] && irender(anim, varseed, speed, basetime, d); glPopMatrix(); @@ -224,6 +230,7 @@ struct md3 : vertmodel } loopv(parts) parts[i]->scaleverts(scale/16.0f, vec(translate.x, -translate.y, translate.z)); radius = calcradius(); + if(shadowdist) calcneighbors(); return loaded = true; } }; diff --git a/source/src/menus.cpp b/source/src/menus.cpp index 3ed9b7a9..62399cfc 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -681,7 +681,7 @@ void rendermenumdl() a[0].name = "weapons/subgun/world"; a[0].tag = "tag_weapon"; } - rendermodel(isplayermodel ? "playermodels" : m.mdl, m.anim, tex, -1, pos, yaw, 0, 0, 0, NULL, a, m.scale ? m.scale/25.0f : 1.0f); + rendermodel(isplayermodel ? "playermodels" : m.mdl, m.anim|ANIM_DYNALLOC, tex, -1, pos, yaw, 0, 0, 0, NULL, a, m.scale ? m.scale/25.0f : 1.0f); glPopMatrix(); } diff --git a/source/src/model.h b/source/src/model.h index 8eed0b84..514c7656 100644 --- a/source/src/model.h +++ b/source/src/model.h @@ -10,6 +10,7 @@ enum { ANIM_IDLE = 0, ANIM_RUN, ANIM_ATTACK, ANIM_PAIN, ANIM_JUMP, ANIM_LAND, AN #define ANIM_NOSKIN (1<<14) #define ANIM_TRANSLUCENT (1<<15) #define ANIM_PARTICLE (1<<16) +#define ANIM_DYNALLOC (1<<17) struct animstate // used for animation blending of animated characters { @@ -38,11 +39,11 @@ struct dynent; struct model { bool cullface; - float alphatest, translucency, scale, radius; + float alphatest, translucency, scale, radius, shadowdist; vec translate; - int batch; + int cachelimit, batch; - model() : cullface(true), alphatest(0.9f), translucency(0.25f), scale(1), radius(0), translate(0, 0, 0), batch(-1) {} + model() : cullface(true), alphatest(0.9f), translucency(0.25f), scale(1), radius(0), shadowdist(0), translate(0, 0, 0), cachelimit(8), batch(-1) {} virtual ~model() {} virtual bool load() = 0; diff --git a/source/src/modelcache.h b/source/src/modelcache.h new file mode 100644 index 00000000..0906cd11 --- /dev/null +++ b/source/src/modelcache.h @@ -0,0 +1,154 @@ +template struct modelcacheentry +{ + typedef modelcacheentry entry; + + T *prev, *next, *nextalloc; + size_t size; + bool locked; + + modelcacheentry(T *prev = NULL, T *next = NULL) : prev(prev), next(next), nextalloc(NULL), size(0), locked(false) {} + + bool empty() const + { + return prev==next; + } + + void linkbefore(T *pos) + { + next = pos; + prev = pos->entry::prev; + prev->entry::next = (T *)this; + next->entry::prev = (T *)this; + } + + void linkafter(T *pos) + { + next = pos->entry::next; + prev = pos; + prev->entry::next = (T *)this; + next->entry::prev = (T *)this; + } + + void unlink() + { + prev->entry::next = next; + next->entry::prev = prev; + prev = next = (T *)this; + } + + void *getdata() + { + return (T *)this + 1; + } +}; + +template struct modelcachelist : modelcacheentry +{ + typedef modelcacheentry entry; + + modelcachelist() : entry((T *)this, (T *)this) {} + + T *start() { return entry::next; } + T *end() { return (T *)this; } + + void addfirst(entry *e) + { + e->linkafter((T *)this); + } + + void addlast(entry *e) + { + e->linkbefore((T *)this); + } + + void removefirst() + { + if(!entry::empty()) entry::next->unlink(); + } + + void removelast() + { + if(!entry::empty()) entry::prev->unlink(); + } +}; + +struct modelcache +{ + struct entry : modelcacheentry {}; + + uchar *buf; + size_t size; + entry *curalloc; + + modelcache(size_t size = 0) : buf(size ? new uchar[size] : NULL), size(size), curalloc(NULL) {} + ~modelcache() { DELETEA(buf); } + + void resize(size_t nsize) + { + if(curalloc) + { + for(curalloc = (entry *)buf; curalloc; curalloc = curalloc->nextalloc) curalloc->unlink(); + } + DELETEA(buf); + buf = nsize ? new uchar[nsize] : 0; + size = nsize; + } + + template + T *allocate(size_t reqsize) + { + reqsize += sizeof(T); + if(reqsize > size) return NULL; + + if(!curalloc) + { + curalloc = (entry *)buf; + curalloc->size = reqsize; + curalloc->locked = false; + curalloc->nextalloc = NULL; + return (T *)curalloc; + } + + if(curalloc) for(bool failed = false;;) + { + uchar *nextfree = curalloc ? (uchar *)curalloc + curalloc->size : (uchar *)buf; + entry *nextused = curalloc ? curalloc->nextalloc : (entry *)buf; + for(;;) + { + if(!nextused) + { + if(size_t(&buf[size] - nextfree) >= reqsize) goto success; + break; + } + else if(size_t((uchar *)nextused - nextfree) >= reqsize) goto success; + else if(nextused->locked) break; + nextused->unlink(); + nextused = nextused->nextalloc; + } + if(curalloc) curalloc->nextalloc = nextused; + else if(failed) { curalloc = nextused; break; } + else failed = true; + curalloc = nextused; + continue; + + success: + entry *result = (entry *)nextfree; + result->size = reqsize; + result->locked = false; + result->nextalloc = nextused; + if(curalloc) curalloc->nextalloc = result; + curalloc = result; + return (T *)curalloc; + } + + curalloc = (entry *)buf; + return NULL; + } + + template + void release(modelcacheentry *e) + { + e->unlink(); + } +}; + diff --git a/source/src/protos.h b/source/src/protos.h index 2286e435..3032eb08 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -2,8 +2,7 @@ #ifndef STANDALONE -extern bool hasTE, hasMT, hasMDA, hasDRE; - +extern bool hasTE, hasMT, hasMDA, hasDRE, hasstencil, hasST2, hasSTW; // GL_ARB_multitexture extern PFNGLACTIVETEXTUREARBPROC glActiveTexture_; extern PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTexture_; @@ -17,6 +16,9 @@ extern PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElements_; // GL_EXT_draw_range_elements extern PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElements_; +// GL_EXT_stencil_two_side +extern PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFace_; + struct color { float r, g, b, alpha; @@ -520,7 +522,7 @@ extern void updateaudio(); // rendermodel extern void rendermodel(const char *mdl, int anim, int tex, float rad, const vec &o, float yaw, float pitch, float speed = 0, int basetime = 0, playerent *d = NULL, modelattach *a = NULL, float scale = 1.0f); extern void startmodelbatches(); -extern void endmodelbatches(); +extern void endmodelbatches(bool flush = true); extern mapmodelinfo &getmminfo(int i); extern int findanim(const char *name); extern void loadskin(const char *dir, const char *altdir, Texture *&skin); diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index 64ef12f3..9ca9d6a7 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -4,7 +4,7 @@ #include "cube.h" #include "bot/bot.h" -bool hasTE = false, hasMT = false, hasMDA = false, hasDRE = false; +bool hasTE = false, hasMT = false, hasMDA = false, hasDRE = false, hasstencil = false, hasST2 = false, hasSTW = false; // GL_ARB_multitexture PFNGLACTIVETEXTUREARBPROC glActiveTexture_ = NULL; @@ -19,6 +19,9 @@ PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElements_ = NULL; // GL_EXT_draw_range_elements PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElements_ = NULL; +// GL_EXT_stencil_two_side +PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFace_ = NULL; + void *getprocaddress(const char *name) { return SDL_GL_GetProcAddress(name); @@ -65,6 +68,20 @@ void gl_checkextensions() hasDRE = true; } + if(strstr(exts, "GL_EXT_stencil_two_side")) + { + glActiveStencilFace_ = (PFNGLACTIVESTENCILFACEEXTPROC)getprocaddress("glActiveStencilFaceEXT"); + hasST2 = true; + } + + if(strstr(exts, "GL_EXT_stencil_wrap")) hasSTW = true; + + if(!hasST2 || !hasSTW) + { + // only enable stencil shadows by default if card is efficient at rendering them + stencilshadow = 0; + } + if(!strstr(exts, "GL_ARB_fragment_program")) { // not a required extension, but ensures the card has enough power to do reflections @@ -802,6 +819,107 @@ void readdepth(int w, int h, vec &pos) pos.z = (float)worldz; } +VARP(stencilshadow, 0, 40, 100); + +int stenciling = 0; + +void drawstencilshadows() +{ + glDisable(GL_FOG); + glEnable(GL_STENCIL_TEST); + glDisable(GL_TEXTURE_2D); + glDepthMask(GL_FALSE); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + stenciling = 1; + + if(hasST2 && hasSTW) + { + glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT); + glDisable(GL_CULL_FACE); + + glActiveStencilFace_(GL_BACK); + glStencilFunc(GL_ALWAYS, 0, ~0U); + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT); + + glActiveStencilFace_(GL_FRONT); + glStencilFunc(GL_ALWAYS, 0, ~0U); + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT); + + startmodelbatches(); + renderentities(); + renderclients(); + renderbounceents(); + endmodelbatches(); + + glEnable(GL_CULL_FACE); + glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + } + else + { + glStencilFunc(GL_ALWAYS, 0, ~0U); + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + + startmodelbatches(); + renderentities(); + renderclients(); + renderbounceents(); + endmodelbatches(false); + + stenciling = 2; + + glStencilFunc(GL_ALWAYS, 0, ~0U); + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); + glCullFace(GL_BACK); + + endmodelbatches(true); + + glCullFace(GL_FRONT); + } + + stenciling = 0; + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glDisable(GL_DEPTH_TEST); + + glStencilFunc(GL_NOTEQUAL, 0, ~0U); + glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_SRC_COLOR); + + float intensity = 1.0f - stencilshadow/100.0f; + glColor3f(intensity, intensity, intensity); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 1, 1, 0, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(1, 0); + glVertex2f(1, 1); + glVertex2f(0, 1); + glEnd(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(fovy, aspect, 0.15f, farplane); + + glMatrixMode(GL_MODELVIEW); + transplayer(); + + glDisable(GL_BLEND); + glDisable(GL_STENCIL_TEST); + glEnable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +} + void gl_drawframe(int w, int h, float changelod, float curfps) { drawminimap(w, h); @@ -816,7 +934,8 @@ void gl_drawframe(int w, int h, float changelod, float curfps) glFogi(GL_FOG_START, (fog+64)/8); glFogi(GL_FOG_END, fog); - float fogc[4] = { (fogcolour>>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f }; + float fogc[4] = { (fogcolour>>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f }, + wfogc[4] = { hdr.watercolor[0]/255.0f, hdr.watercolor[1]/255.0f, hdr.watercolor[2]/255.0f, 1.0f }; glFogfv(GL_FOG_COLOR, fogc); glClearColor(fogc[0], fogc[1], fogc[2], 1.0f); @@ -827,7 +946,6 @@ void gl_drawframe(int w, int h, float changelod, float curfps) { fovy += sinf(lastmillis/1000.0f)*2.0f; aspect += sinf(lastmillis/1000.0f+PI)*0.1f; - float wfogc[4] = { hdr.watercolor[0]/255.0f, hdr.watercolor[1]/255.0f, hdr.watercolor[2]/255.0f, 1.0f }; glFogfv(GL_FOG_COLOR, wfogc); glFogi(GL_FOG_START, 0); glFogi(GL_FOG_END, (fog+96)/8); @@ -851,7 +969,7 @@ void gl_drawframe(int w, int h, float changelod, float curfps) } } - glClear((outsidemap(camera1) ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); + glClear((outsidemap(camera1) ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_TEXTURE_2D); @@ -900,6 +1018,8 @@ void gl_drawframe(int w, int h, float changelod, float curfps) renderbounceents(); endmodelbatches(); + if(stencilshadow && hasstencil) drawstencilshadows(); + // Added by Rick: Need todo here because of drawing the waypoints glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); WaypointClass.Think(); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index ba634fc1..f55e7ff8 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -6,6 +6,7 @@ VARP(animationinterpolationtime, 0, 150, 1000); model *loadingmodel = NULL; #include "tristrip.h" +#include "modelcache.h" #include "vertmodel.h" #include "md2.h" #include "md3.h" @@ -55,6 +56,22 @@ void mdltrans(char *x, char *y, char *z) COMMAND(mdltrans, ARG_3STR); +void mdlshadowdist(int dist) +{ + checkmdl; + loadingmodel->shadowdist = dist; +} + +COMMAND(mdlshadowdist, ARG_1INT); + +void mdlcachelimit(int limit) +{ + checkmdl; + loadingmodel->cachelimit = limit; +} + +COMMAND(mdlcachelimit, ARG_1INT); + vector mapmodels; void mapmodel(char *rad, char *h, char *zoff, char *snap, char *name) @@ -165,6 +182,15 @@ batchedmodel &addbatchedmodel(model *m) void renderbatchedmodel(model *m, batchedmodel &b) { + modelattach *a = NULL; + if(b.attached>=0) a = &modelattached[b.attached]; + + if(stenciling) + { + m->render(b.anim|ANIM_NOSKIN, b.varseed, b.speed, b.basetime, b.o, b.yaw, b.pitch, b.d, a, b.scale); + return; + } + int x = (int)b.o.x, y = (int)b.o.y; if(!OUTBORD(x, y)) { @@ -175,8 +201,6 @@ void renderbatchedmodel(model *m, batchedmodel &b) m->setskin(b.tex); - modelattach *a = NULL; - if(b.attached>=0) a = &modelattached[b.attached]; if(b.anim&ANIM_TRANSLUCENT) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); @@ -236,7 +260,7 @@ static int sorttranslucentmodels(const translucentmodel *x, const translucentmod return 0; } -void endmodelbatches() +void endmodelbatches(bool flush) { vector translucent; loopi(numbatches) @@ -258,7 +282,7 @@ void endmodelbatches() } renderbatchedmodel(b.m, bm); } - if(dynshadow && b.m->hasshadows() && (!reflecting || refracting)) + if(dynshadow && b.m->hasshadows() && (!reflecting || refracting) && (!stencilshadow || !hasstencil)) { glColor4f(0, 0, 0, dynshadow/100.0f); loopvj(b.batched) @@ -286,13 +310,13 @@ void endmodelbatches() } if(lastmodel) lastmodel->endrender(); } - numbatches = -1; + if(flush) numbatches = -1; } 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) { model *m = loadmodel(mdl); - if(!m) return; + if(!m || (stenciling && (m->shadowdist <= 0 || anim&ANIM_TRANSLUCENT))) return; if(rad >= 0) { @@ -332,13 +356,21 @@ void rendermodel(const char *mdl, int anim, int tex, float rad, const vec &o, fl return; } + if(stenciling) + { + m->startrender(); + m->render(anim|ANIM_NOSKIN, varseed, speed, basetime, o, yaw, pitch, d, a, scale); + m->endrender(); + return; + } + m->startrender(); int x = (int)o.x, y = (int)o.y; if(!OUTBORD(x, y)) { sqr *s = S(x, y); - if(!(anim&ANIM_TRANSLUCENT) && dynshadow && m->hasshadows() && (!reflecting || refracting)) + if(!(anim&ANIM_TRANSLUCENT) && dynshadow && m->hasshadows() && (!reflecting || refracting) && (!stencilshadow || !hasstencil)) { vec center(o.x, o.y, s->floor); if(s->type==FHF) center.z -= s->vdelta/4.0f; @@ -488,14 +520,14 @@ void renderclient(playerent *d, const char *mdlname, const char *vwepname, int t a[0].name = vwepname; a[0].tag = "tag_weapon"; - if(d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction < d->weaponsel->flashtime()) + if(!stenciling && !reflecting && !refracting && d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction < d->weaponsel->flashtime()) anim |= ANIM_PARTICLE; } // FIXME: while networked my state as spectator seems to stay CS_DEAD, not CS_SPECTATE // flowtron: I fixed this for following at least (see followplayer()) if(player1->isspectating() && d->clientnum == player1->followplayercn && player1->spectatemode == SM_FOLLOW3RD_TRANSPARENT) anim |= ANIM_TRANSLUCENT; // see through followed player - rendermodel(mdlname, anim, tex, 1.5f, o, d->yaw+90, d->pitch/4, speed, basetime, d, a); - if(isteam(player1->team, d->team)) renderaboveheadicon(d); + rendermodel(mdlname, anim|ANIM_DYNALLOC, tex, 1.5f, o, d->yaw+90, d->pitch/4, speed, basetime, d, a); + if(!stenciling && !reflecting && !refracting && isteam(player1->team, d->team)) renderaboveheadicon(d); } VARP(teamdisplaymode, 0, 1, 2); diff --git a/source/src/tools.h b/source/src/tools.h index cbce2142..9f88f2f2 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -69,6 +69,7 @@ static inline float round(float x) { return floor(x + 0.5f); } #define PI (3.1415927f) #define PI2 (2*PI) +#define SQRT2 (1.4142136f) #define SQRT3 (1.7320508f) #define RAD (PI / 180.0f) diff --git a/source/src/vertmodel.h b/source/src/vertmodel.h index 870c1fc7..2d103615 100644 --- a/source/src/vertmodel.h +++ b/source/src/vertmodel.h @@ -2,6 +2,11 @@ VARP(dynshadowsize, 4, 5, 8); VARP(aadynshadow, 0, 2, 3); VARP(saveshadows, 0, 1, 1); +VAR(shadowyaw, 0, 45, 360); +vec shadowdir(0, 0, -1), shadowpos(0, 0, 0); + +VAR(dbgstenc, 0, 0, 1); + struct vertmodel : model { struct anpos @@ -34,40 +39,60 @@ struct vertmodel : model } bool operator==(const anpos &a) const { return fr1==a.fr1 && fr2==a.fr2 && (fr1==fr2 || t==a.t); } + bool operator!=(const anpos &a) const { return fr1!=a.fr1 || fr2!=a.fr2 || (fr1!=fr2 && t!=a.t); } }; struct tcvert { float u, v; }; - struct tri { ushort vert[3]; }; + struct tri { ushort vert[3]; ushort neighbor[3]; }; struct part; typedef tristrip::drawcall drawcall; + struct dyncacheentry : modelcacheentry + { + anpos cur, prev; + float t; + + vec *verts() { return (vec *)getdata(); } + int numverts() { return int((size - sizeof(dyncacheentry)) / sizeof(vec)); } + }; + + struct shadowcacheentry : modelcacheentry + { + anpos cur, prev; + float t; + vec dir; + + ushort *idxs() { return (ushort *)getdata(); } + int numidxs() { return int((size - sizeof(shadowcacheentry)) / sizeof(ushort)); } + }; + struct mesh { char *name; part *owner; vec *verts; tcvert *tcverts; + ushort *shareverts; tri *tris; int numverts, numtris; Texture *skin; int tex; - vec *dynbuf; + modelcachelist dyncache; + modelcachelist shadowcache; + ushort *dynidx; int dynlen; drawcall *dyndraws; int numdyndraws; - anpos dyncur, dynprev; - float dynt; GLuint statlist; int statlen; - mesh() : name(0), owner(0), verts(0), tcverts(0), tris(0), skin(notexture), tex(0), dynbuf(0), dynidx(0), dyndraws(0), statlist(0) + mesh() : name(0), owner(0), verts(0), tcverts(0), shareverts(0), tris(0), skin(notexture), tex(0), dynidx(0), dyndraws(0), statlist(0) { - dyncur.fr1 = dynprev.fr1 = -1; } ~mesh() @@ -75,10 +100,10 @@ struct vertmodel : model DELETEA(name); DELETEA(verts); DELETEA(tcverts); + DELETEA(shareverts); DELETEA(tris); if(statlist) glDeleteLists(statlist, 1); DELETEA(dynidx); - DELETEA(dynbuf); DELETEA(dyndraws); } @@ -89,7 +114,6 @@ struct vertmodel : model vector idxs; vector draws; ts.buildstrips(idxs, draws); - dynbuf = new vec[numverts]; dynidx = new ushort[idxs.length()]; memcpy(dynidx, idxs.getbuf(), idxs.length()*sizeof(ushort)); dynlen = idxs.length(); @@ -98,29 +122,43 @@ struct vertmodel : model numdyndraws = draws.length(); } - void gendynverts(anpos &cur, anpos *prev, float ai_t) + dyncacheentry *gendynverts(animstate &as, anpos &cur, anpos *prev, float ai_t) { - vec *vert1 = &verts[cur.fr1 * numverts], + dyncacheentry *d = dyncache.start(); + int cachelen = 0; + for(; d != dyncache.end(); d = d->next, cachelen++) + { + if(d->cur != cur) continue; + if(prev) + { + if(d->prev == *prev && d->t == ai_t) return d; + } + else if(d->prev.fr1 < 0) return d; + } + + d = dynalloc.allocate((numverts + 1)*sizeof(vec)); + if(!d) return NULL; + + if(cachelen >= owner->model->cachelimit) dyncache.removelast(); + dyncache.addfirst(d); + vec *buf = d->verts(), + *vert1 = &verts[cur.fr1 * numverts], *vert2 = &verts[cur.fr2 * numverts], *pvert1 = NULL, *pvert2 = NULL; + d->cur = cur; + d->t = ai_t; if(prev) { - if(dynprev==*prev && dyncur==cur && dynt==ai_t) return; - dynprev = *prev; - dynt = ai_t; + d->prev = *prev; pvert1 = &verts[prev->fr1 * numverts]; pvert2 = &verts[prev->fr2 * numverts]; } - else - { - if(dynprev.fr1<0 && dyncur==cur) return; - dynprev.fr1 = -1; - } - dyncur = cur; + else d->prev.fr1 = -1; + #define iploop(body) \ loopi(numverts) \ { \ - vec &v = dynbuf[i]; \ + vec &v = buf[i]; \ body; \ } #define ip(p1, p2, t) (p1+t*(p2-p1)) @@ -131,6 +169,63 @@ struct vertmodel : model #undef ip #undef ip_v #undef ip_v_ai + + if(d->verts() == lastvertexarray) lastvertexarray = (void *)-1; + + return d; + } + + void weldverts() + { + hashtable idxs; + shareverts = new ushort[numverts]; + loopi(numverts) shareverts[i] = (ushort)idxs.access(verts[i], i); + for(int i = 1; i < owner->numframes; i++) + { + const vec *frame = &verts[i*numverts]; + loopj(numverts) + { + if(frame[j] != frame[shareverts[j]]) shareverts[j] = (ushort)j; + } + } + } + + void findneighbors() + { + hashtable edges; + loopi(numtris) + { + const tri &t = tris[i]; + loopj(3) + { + uint e1 = shareverts[t.vert[j]], e2 = shareverts[t.vert[(j+1)%3]], shift = 0; + if(e1 > e2) { swap(e1, e2); shift = 16; } + uint &edge = edges.access(e1 | (e2<<16), ~0U); + if(((edge>>shift)&0xFFFF) != 0xFFFF) edge = 0; + else + { + edge &= 0xFFFF<<(16-shift); + edge |= i< e2) { swap(e1, e2); shift = 16; } + uint edge = edges[e1 | (e2<<16)]; + if(!edge || int((edge>>shift)&0xFFFF)!=i) t.neighbor[j] = 0xFFFF; + else + { + t.neighbor[j] = (edge>>(16-shift))&0xFFFF; + //ushort n = (edge>>(16-shift))&0xFFFF; + //t.neighbor[j] = n;//n==0xFFFF ? i : n;//(edge>>(16-shift))&0xFFFF; + } + } + } } void cleanup() @@ -142,10 +237,69 @@ struct vertmodel : model } } + shadowcacheentry *genshadowvolume(animstate &as, anpos &cur, anpos *prev, float ai_t, vec *buf) + { + if(!shareverts) return NULL; + + shadowcacheentry *d = shadowcache.start(); + int cachelen = 0; + for(; d != shadowcache.end(); d = d->next, cachelen++) + { + if(d->dir != shadowpos || d->cur != cur) continue; + if(prev) + { + if(d->prev == *prev && d->t == ai_t) return d; + } + else if(d->prev.fr1 < 0) return d; + } + + d = (owner->numframes > 1 || as.anim&ANIM_DYNALLOC ? dynalloc : statalloc).allocate(9*numtris*sizeof(ushort)); + if(!d) return NULL; + + if(cachelen >= owner->model->cachelimit) shadowcache.removelast(); + shadowcache.addfirst(d); + d->dir = shadowpos; + d->cur = cur; + d->t = ai_t; + if(prev) d->prev = *prev; + else d->prev.fr1 = -1; + + static vector side; + side.setsizenodelete(0); + + loopi(numtris) + { + const tri &t = tris[i]; + const vec &a = buf[t.vert[0]], &b = buf[t.vert[1]], &c = buf[t.vert[2]]; + side.add(vec().cross(vec(b).sub(a), vec(c).sub(a)).dot(shadowdir) <= 0 ? 1 : 0); + } + + ushort *idx = d->idxs(); + loopi(numtris) if(side[i]) + { + const tri &t = tris[i]; + loopj(3) + { + ushort n = t.neighbor[j]; + if(n==0xFFFF || !side[n]) + { + ushort e1 = shareverts[t.vert[j]], e2 = shareverts[t.vert[(j+1)%3]]; + if(!side[i]) swap(e1, e2); + *idx++ = e2; + *idx++ = e1; + *idx++ = numverts; + } + } + } + + if(dbgstenc && stenciling==1) conoutf("%s: %d tris\n", owner->filename, (idx - d->idxs())/3); + + d->size = (uchar *)idx - (uchar *)d; + return d; + } + void render(animstate &as, anpos &cur, anpos *prev, float ai_t) { - if(!dynbuf) return; - if(!(as.anim&ANIM_NOSKIN)) { GLuint id = tex < 0 ? -tex : skin->id; @@ -170,21 +324,28 @@ struct vertmodel : model } bool isstat = as.frame==0 && as.range==1; - if(isstat && statlist) + if(isstat && statlist && !stenciling) { glCallList(statlist); xtraverts += statlen; } else { - gendynverts(cur, prev, ai_t); - if(lastvertexarray != dynbuf) + vec *buf = verts; + dyncacheentry *d = NULL; + if(!isstat) + { + d = gendynverts(as, cur, prev, ai_t); + if(!d) return; + buf = d->verts(); + } + if(lastvertexarray != buf) { if(!lastvertexarray) glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, sizeof(vec), dynbuf); - lastvertexarray = dynbuf; + glVertexPointer(3, GL_FLOAT, sizeof(vec), buf); + lastvertexarray = buf; } - if(as.anim&ANIM_NOSKIN && !isstat) + if(as.anim&ANIM_NOSKIN && (!isstat || stenciling)) { if(lasttexcoordarray) { @@ -198,6 +359,19 @@ struct vertmodel : model glTexCoordPointer(2, GL_FLOAT, sizeof(tcvert), tcverts); lasttexcoordarray = tcverts; } + if(stenciling) + { + if(d) d->locked = true; + shadowcacheentry *s = genshadowvolume(as, cur, isstat ? NULL : prev, ai_t, buf); + if(d) d->locked = false; + if(!s) return; + buf[numverts] = s->dir; + glVertexPointer(3, GL_FLOAT, sizeof(vec), buf); + glDrawElements(GL_TRIANGLES, s->numidxs(), GL_UNSIGNED_SHORT, s->idxs()); + xtraverts += s->numidxs(); + return; + } + if(isstat) glNewList(statlist = glGenLists(1), GL_COMPILE); loopi(numdyndraws) { @@ -212,6 +386,7 @@ struct vertmodel : model statlen = dynlen; } xtraverts += dynlen; + return; } } @@ -238,6 +413,15 @@ struct vertmodel : model loopi(numverts) rad = max(rad, verts[i].magnitudexy()); return rad; } + + void calcneighbors() + { + if(!shareverts) + { + weldverts(); + findneighbors(); + } + } }; struct animinfo @@ -484,7 +668,28 @@ struct vertmodel : model } return true; } - + + void calcnormal(GLfloat *m, vec &dir) + { + vec n(dir); + dir.x = n.x*m[0] + n.y*m[1] + n.z*m[2]; + dir.y = n.x*m[4] + n.y*m[5] + n.z*m[6]; + dir.z = n.x*m[8] + n.y*m[9] + n.z*m[10]; + } + + void calcvertex(GLfloat *m, vec &pos) + { + vec p(pos); + + p.x -= m[12]; + p.y -= m[13]; + p.z -= m[14]; + + pos.x = p.x*m[0] + p.y*m[1] + p.z*m[2]; + pos.y = p.x*m[4] + p.y*m[5] + p.z*m[6]; + pos.z = p.x*m[8] + p.y*m[9] + p.z*m[10]; + } + void render(int anim, int varseed, float speed, int basetime, dynent *d) { if(meshes.empty()) return; @@ -514,10 +719,26 @@ struct vertmodel : model part *link = links[i]; if(link) { + vec oldshadowdir, oldshadowpos; + + if(stenciling) + { + oldshadowdir = shadowdir; + oldshadowpos = shadowpos; + calcnormal(matrix, shadowdir); + calcvertex(matrix, shadowpos); + } + glPushMatrix(); glMultMatrixf(matrix); link->render(anim, varseed, speed, basetime, d); glPopMatrix(); + + if(stenciling) + { + shadowdir = oldshadowdir; + shadowpos = oldshadowpos; + } } if(anim&ANIM_PARTICLE && emitters && emitters[i].type>=0) @@ -732,6 +953,11 @@ struct vertmodel : model loopv(meshes) rad = max(rad, meshes[i]->calcradius()); return rad; } + + void calcneighbors() + { + loopv(meshes) meshes[i]->calcneighbors(); + } }; bool loaded; @@ -785,6 +1011,11 @@ struct vertmodel : model return parts.empty() ? 0.0f : parts[0]->calcradius(); } + void calcneighbors() + { + loopv(parts) parts[i]->calcneighbors(); + } + static bool enablealphablend, enablealphatest, enabledepthmask; static GLuint lasttex; static float lastalphatest; @@ -807,6 +1038,8 @@ struct vertmodel : model if(lastvertexarray) glDisableClientState(GL_VERTEX_ARRAY); if(lasttexcoordarray) glDisableClientState(GL_TEXTURE_COORD_ARRAY); } + + static modelcache dynalloc, statalloc; }; bool vertmodel::enablealphablend = false, vertmodel::enablealphatest = false, vertmodel::enabledepthmask = true; @@ -814,3 +1047,8 @@ GLuint vertmodel::lasttex = 0; float vertmodel::lastalphatest = -1; void *vertmodel::lastvertexarray = NULL, *vertmodel::lasttexcoordarray = NULL; +VARF(mdldyncache, 1, 2, 32, vertmodel::dynalloc.resize(mdldyncache<<20)); +VARF(mdlstatcache, 1, 1, 32, vertmodel::statalloc.resize(mdlstatcache<<20)); + +modelcache vertmodel::dynalloc(mdldyncache<<20), vertmodel::statalloc(mdlstatcache<<20); + diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 1501d957..48f0247f 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -336,7 +336,7 @@ void renderbounceents() } } path(model); - rendermodel(model, ANIM_MAPMODEL|ANIM_LOOP, 0, 1.1f, o, p->yaw+90, p->pitch); + rendermodel(model, ANIM_MAPMODEL|ANIM_LOOP|ANIM_DYNALLOC, 0, 1.1f, o, p->yaw+90, p->pitch); } } @@ -544,7 +544,7 @@ void weapon::renderhudmodel(int lastaction, int index) lastanim[index] = wm.anim|(type<<24); lastswitch[index] = lastmillis; } - rendermodel(path, wm.anim|(index ? ANIM_MIRROR : 0)|(emit ? ANIM_PARTICLE : 0), 0, -1, wm.pos, player1->yaw+90, player1->pitch+wm.k_rot, 40.0f, lastswitch[index], NULL, NULL, 1.28f); + rendermodel(path, wm.anim|ANIM_DYNALLOC|(index ? ANIM_MIRROR : 0)|(emit ? ANIM_PARTICLE : 0), 0, -1, wm.pos, player1->yaw+90, player1->pitch+wm.k_rot, 40.0f, lastswitch[index], NULL, NULL, 1.28f); } void weapon::updatetimers()