static inline void GL_DrawLeaf( mleaf_t *leaf ) { mface_t **face, **last; if( leaf->contents == CONTENTS_SOLID ) { return; // solid leaf } if( glr.fd.areabits && !Q_IsBitSet( glr.fd.areabits, leaf->area ) ) { return; // door blocks sight } last = leaf->firstleafface + leaf->numleaffaces; for( face = leaf->firstleafface; face < last; face++ ) { (*face)->drawframe = glr.drawframe; } c.leavesDrawn++; }
/* =============== SV_EdictIsVisible Checks if edict is potentially visible from the given PVS row. =============== */ qboolean SV_EdictIsVisible(cm_t *cm, edict_t *ent, byte *mask) { int i; if (ent->num_clusters == -1) { // too many leafs for individual check, go by headnode return CM_HeadnodeVisible(CM_NodeNum(cm, ent->headnode), mask); } // check individual leafs for (i = 0; i < ent->num_clusters; i++) { if (Q_IsBitSet(mask, ent->clusternums[i])) { return qtrue; } } return qfalse; // not visible }
void SV_AutoSaveBegin(mapcmd_t *cmd) { byte bitmap[MAX_CLIENTS / CHAR_BIT]; edict_t *ent; int i; // check for clearing the current savegame if (cmd->endofunit) { wipe_save_dir(SAVE_CURRENT); return; } if (sv.state != ss_game) return; if (no_save_games()) return; memset(bitmap, 0, sizeof(bitmap)); // clear all the client inuse flags before saving so that // when the level is re-entered, the clients will spawn // at spawn points instead of occupying body shells for (i = 0; i < sv_maxclients->integer; i++) { ent = EDICT_NUM(i + 1); if (ent->inuse) { Q_SetBit(bitmap, i); ent->inuse = qfalse; } } // save the map just exited if (write_level_file()) Com_EPrintf("Couldn't write level file.\n"); // we must restore these for clients to transfer over correctly for (i = 0; i < sv_maxclients->integer; i++) { ent = EDICT_NUM(i + 1); ent->inuse = Q_IsBitSet(bitmap, i); } }
/* ================ R_RecursiveClipBPoly Clip a bmodel poly down the world bsp tree ================ */ static void R_RecursiveClipBPoly(bedge_t *pedges, mnode_t *pnode, mface_t *psurf) { bedge_t *psideedges[2], *pnextedge, *ptedge; int i, side, lastside; float dist, frac, lastdist; cplane_t *splitplane, tplane; mvertex_t *pvert, *plastvert, *ptvert; mnode_t *pn; int area; psideedges[0] = psideedges[1] = NULL; makeclippededge = qfalse; // transform the BSP plane into model space // FIXME: cache these? splitplane = pnode->plane; tplane.dist = -PlaneDiff(r_entorigin, splitplane); tplane.normal[0] = DotProduct(entity_rotation[0], splitplane->normal); tplane.normal[1] = DotProduct(entity_rotation[1], splitplane->normal); tplane.normal[2] = DotProduct(entity_rotation[2], splitplane->normal); // clip edges to BSP plane for (; pedges; pedges = pnextedge) { pnextedge = pedges->pnext; // set the status for the last point as the previous point // FIXME: cache this stuff somehow? plastvert = pedges->v[0]; lastdist = PlaneDiff(plastvert->point, &tplane); if (lastdist > 0) lastside = 0; else lastside = 1; pvert = pedges->v[1]; dist = PlaneDiff(pvert->point, &tplane); if (dist > 0) side = 0; else side = 1; if (side != lastside) { // clipped if (numbverts >= MAX_BMODEL_VERTS) return; // generate the clipped vertex frac = lastdist / (lastdist - dist); ptvert = &pbverts[numbverts++]; LerpVector(plastvert->point, pvert->point, frac, ptvert->point); // split into two edges, one on each side, and remember entering // and exiting points // FIXME: share the clip edge by having a winding direction flag? if (numbedges >= (MAX_BMODEL_EDGES - 1)) { Com_Printf("Out of edges for bmodel\n"); return; } ptedge = &pbedges[numbedges]; ptedge->pnext = psideedges[lastside]; psideedges[lastside] = ptedge; ptedge->v[0] = plastvert; ptedge->v[1] = ptvert; ptedge = &pbedges[numbedges + 1]; ptedge->pnext = psideedges[side]; psideedges[side] = ptedge; ptedge->v[0] = ptvert; ptedge->v[1] = pvert; numbedges += 2; if (side == 0) { // entering for front, exiting for back pfrontenter = ptvert; makeclippededge = qtrue; } else { pfrontexit = ptvert; makeclippededge = qtrue; } } else { // add the edge to the appropriate side pedges->pnext = psideedges[side]; psideedges[side] = pedges; } } // if anything was clipped, reconstitute and add the edges along the clip // plane to both sides (but in opposite directions) if (makeclippededge) { if (numbedges >= (MAX_BMODEL_EDGES - 2)) { Com_Error(ERR_DROP, "Out of edges for bmodel"); } ptedge = &pbedges[numbedges]; ptedge->pnext = psideedges[0]; psideedges[0] = ptedge; ptedge->v[0] = pfrontexit; ptedge->v[1] = pfrontenter; ptedge = &pbedges[numbedges + 1]; ptedge->pnext = psideedges[1]; psideedges[1] = ptedge; ptedge->v[0] = pfrontenter; ptedge->v[1] = pfrontexit; numbedges += 2; } // draw or recurse further for (i = 0; i < 2; i++) { if (psideedges[i]) { // draw if we've reached a non-solid leaf, done if all that's left is a // solid leaf, and continue down the tree if it's not a leaf pn = pnode->children[i]; // we're done with this branch if the node or leaf isn't in the PVS if (pn->visframe == r_visframecount) { if (!pn->plane) { mleaf_t *pl = (mleaf_t *)pn; if (pl->contents != CONTENTS_SOLID) { if (r_newrefdef.areabits) { area = pl->area; if (!Q_IsBitSet(r_newrefdef.areabits, area)) continue; // not visible } r_currentbkey = pl->key; R_RenderBmodelFace(psideedges[i], psurf); } } else { R_RecursiveClipBPoly(psideedges[i], pnode->children[i], psurf); } } } } }
/* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode(mnode_t *node, int clipflags) { int i, c, side, *pindex; vec3_t acceptpt, rejectpt; cplane_t *plane; mface_t *surf, **mark; float d, dot; mleaf_t *pleaf; while (node->visframe == r_visframecount) { // cull the clipping planes if not trivial accept // FIXME: the compiler is doing a lousy job of optimizing here; it could be // twice as fast in ASM if (clipflags) { for (i = 0; i < 4; i++) { if (!(clipflags & (1 << i))) continue; // don't need to clip against it // generate accept and reject points // FIXME: do with fast look-ups or integer tests based on the sign bit // of the floating point values pindex = pfrustum_indexes[i]; rejectpt[0] = (float)node->minmaxs[pindex[0]]; rejectpt[1] = (float)node->minmaxs[pindex[1]]; rejectpt[2] = (float)node->minmaxs[pindex[2]]; d = PlaneDiff(rejectpt, &view_clipplanes[i]); if (d <= 0) return; acceptpt[0] = (float)node->minmaxs[pindex[3 + 0]]; acceptpt[1] = (float)node->minmaxs[pindex[3 + 1]]; acceptpt[2] = (float)node->minmaxs[pindex[3 + 2]]; d = PlaneDiff(acceptpt, &view_clipplanes[i]); if (d >= 0) clipflags &= ~(1 << i); // node is entirely on screen } } c_drawnode++; // if a leaf node, draw stuff if (!node->plane) { pleaf = (mleaf_t *)node; if (pleaf->contents == CONTENTS_SOLID) return; // solid // check for door connected areas if (r_newrefdef.areabits) { if (! Q_IsBitSet(r_newrefdef.areabits, pleaf->area)) return; // not visible } mark = pleaf->firstleafface; c = pleaf->numleaffaces; if (c) { do { (*mark)->drawframe = r_framecount; mark++; } while (--c); } pleaf->key = r_currentkey; r_currentkey++; // all bmodels in a leaf share the same key return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = node->plane; dot = PlaneDiffFast(modelorg, plane); if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode(node->children[side], clipflags); // draw stuff c = node->numfaces; if (c) { surf = node->firstface; if (dot < -BACKFACE_EPSILON) { do { if ((surf->drawflags & DSURF_PLANEBACK) && (surf->drawframe == r_framecount)) { R_RenderFace(surf, clipflags); } surf++; } while (--c); } else if (dot > BACKFACE_EPSILON) { do { if (!(surf->drawflags & DSURF_PLANEBACK) && (surf->drawframe == r_framecount)) { R_RenderFace(surf, clipflags); } surf++; } while (--c); } // all surfaces on the same node share the same sequence number r_currentkey++; } // recurse down the back side node = node->children[side ^ 1]; } }
/* =================== Key_Event Called by the system between frames for both key up and key down events Should NOT be called during an interrupt! =================== */ void Key_Event(unsigned key, qboolean down, unsigned time) { char *kb; char cmd[MAX_STRING_CHARS]; if (key >= 256) { Com_Error(ERR_FATAL, "%s: bad key", __func__); } Com_DDDPrintf("%u: %c%s\n", time, down ? '+' : '-', Key_KeynumToString(key)); // hack for menu key binding if (key_wait_cb && down && !key_wait_cb(key_wait_arg, key)) { return; } // update key down and auto-repeat status if (down) { if (keydown[key] < 255) keydown[key]++; } else { keydown[key] = 0; } // console key is hardcoded, so the user can never unbind it if (!Key_IsDown(K_SHIFT) && (key == '`' || key == '~')) { if (keydown[key] == 1) { Con_ToggleConsole_f(); } return; } // Alt+Enter is hardcoded for all systems if (Key_IsDown(K_ALT) && key == K_ENTER) { if (keydown[key] == 1) { VID_ToggleFullscreen(); } return; } // menu key is hardcoded, so the user can never unbind it if (key == K_ESCAPE) { if (!down) { return; } if (cls.key_dest == KEY_GAME && cl.frame.ps.stats[STAT_LAYOUTS] && cls.demo.playback == qfalse) { if (keydown[key] == 2) { // force main menu if escape is held UI_OpenMenu(UIMENU_GAME); } else if (keydown[key] == 1) { // put away help computer / inventory CL_ClientCommand("putaway"); } return; } // ignore autorepeats if (keydown[key] > 1) { return; } if (cls.key_dest & KEY_CONSOLE) { if (cls.state < ca_active && !(cls.key_dest & KEY_MENU)) { UI_OpenMenu(UIMENU_MAIN); } else { Con_Close(qtrue); } } else if (cls.key_dest & KEY_MENU) { UI_Keydown(key); } else if (cls.key_dest & KEY_MESSAGE) { Key_Message(key); } else if (cls.state == ca_active) { UI_OpenMenu(UIMENU_GAME); } else { UI_OpenMenu(UIMENU_MAIN); } return; } // track if any key is down for BUTTON_ANY if (down) { if (keydown[key] == 1) anykeydown++; } else { anykeydown--; if (anykeydown < 0) anykeydown = 0; } // hack for demo freelook in windowed mode if (cls.key_dest == KEY_GAME && cls.demo.playback && key == K_SHIFT && keydown[key] <= 1) { IN_Activate(); } // // if not a consolekey, send to the interpreter no matter what mode is // if ((cls.key_dest == KEY_GAME) || ((cls.key_dest & KEY_CONSOLE) && !Q_IsBitSet(consolekeys, key)) || ((cls.key_dest & KEY_MENU) && (key >= K_F1 && key <= K_F12)) || (!down && Q_IsBitSet(buttondown, key))) { // // Key up events only generate commands if the game key binding is a button // command (leading + sign). These will occur even in console mode, to keep the // character from continuing an action started before a console switch. Button // commands include the kenum as a parameter, so multiple downs can be matched // with ups. // if (!down) { kb = keybindings[key]; if (kb && kb[0] == '+') { Q_snprintf(cmd, sizeof(cmd), "-%s %i %i\n", kb + 1, key, time); Cbuf_AddText(&cmd_buffer, cmd); } Q_ClearBit(buttondown, key); return; } // ignore autorepeats if (keydown[key] > 1) { return; } // generate button up command when released Q_SetBit(buttondown, key); kb = keybindings[key]; if (kb) { if (kb[0] == '+') { // button commands add keynum and time as a parm Q_snprintf(cmd, sizeof(cmd), "%s %i %i\n", kb, key, time); Cbuf_AddText(&cmd_buffer, cmd); } else { Cbuf_AddText(&cmd_buffer, kb); Cbuf_AddText(&cmd_buffer, "\n"); } } return; } if (cls.key_dest == KEY_GAME) return; if (!down) return; // other subsystems only care about key down events if (cls.key_dest & KEY_CONSOLE) { Key_Console(key); } else if (cls.key_dest & KEY_MENU) { UI_Keydown(key); } else if (cls.key_dest & KEY_MESSAGE) { Key_Message(key); } if (Key_IsDown(K_CTRL) || Key_IsDown(K_ALT)) { return; } switch (key) { case K_KP_SLASH: key = '/'; break; case K_KP_MULTIPLY: key = '*'; break; case K_KP_MINUS: key = '-'; break; case K_KP_PLUS: key = '+'; break; case K_KP_HOME: key = '7'; break; case K_KP_UPARROW: key = '8'; break; case K_KP_PGUP: key = '9'; break; case K_KP_LEFTARROW: key = '4'; break; case K_KP_5: key = '5'; break; case K_KP_RIGHTARROW: key = '6'; break; case K_KP_END: key = '1'; break; case K_KP_DOWNARROW: key = '2'; break; case K_KP_PGDN: key = '3'; break; case K_KP_INS: key = '0'; break; case K_KP_DEL: key = '.'; break; } // if key is printable, generate char events if (key < 32 || key >= 127) { return; } if (Key_IsDown(K_SHIFT)) { key = keyshift[key]; } if (cls.key_dest & KEY_CONSOLE) { Char_Console(key); } else if (cls.key_dest & KEY_MENU) { UI_CharEvent(key); } else if (cls.key_dest & KEY_MESSAGE) { Char_Message(key); } }
static void GL_MarkLeaves( void ) { static int lastNodesVisible; byte vis1[VIS_MAX_BYTES]; byte vis2[VIS_MAX_BYTES]; mleaf_t *leaf; mnode_t *node; uint_fast32_t *src1, *src2; int cluster1, cluster2, longs; vec3_t tmp; int i; bsp_t *bsp = gl_static.world.cache; leaf = BSP_PointLeaf( bsp->nodes, glr.fd.vieworg ); cluster1 = cluster2 = leaf->cluster; VectorCopy( glr.fd.vieworg, tmp ); if( !leaf->contents ) { tmp[2] -= 16; } else { tmp[2] += 16; } leaf = BSP_PointLeaf( bsp->nodes, tmp ); if( !( leaf->contents & CONTENTS_SOLID ) ) { cluster2 = leaf->cluster; } if( cluster1 == glr.viewcluster1 && cluster2 == glr.viewcluster2 ) { goto finish; } if( gl_lockpvs->integer ) { goto finish; } glr.visframe++; glr.viewcluster1 = cluster1; glr.viewcluster2 = cluster2; if( !bsp->vis || gl_novis->integer || cluster1 == -1 ) { // mark everything visible for( i = 0; i < bsp->numnodes; i++ ) { bsp->nodes[i].visframe = glr.visframe; } for( i = 0; i < bsp->numleafs; i++ ) { bsp->leafs[i].visframe = glr.visframe; } lastNodesVisible = bsp->numnodes; goto finish; } BSP_ClusterVis( bsp, vis1, cluster1, DVIS_PVS ); if( cluster1 != cluster2 ) { BSP_ClusterVis( bsp, vis2, cluster2, DVIS_PVS ); longs = VIS_FAST_LONGS( bsp ); src1 = ( uint_fast32_t * )vis1; src2 = ( uint_fast32_t * )vis2; while( longs-- ) { *src1++ |= *src2++; } } lastNodesVisible = 0; for( i = 0, leaf = bsp->leafs; i < bsp->numleafs; i++, leaf++ ) { cluster1 = leaf->cluster; if( cluster1 == -1 ) { continue; } if( Q_IsBitSet( vis1, cluster1 ) ) { node = ( mnode_t * )leaf; // mark parent nodes visible do { if( node->visframe == glr.visframe ) { break; } node->visframe = glr.visframe; node = node->parent; lastNodesVisible++; } while( node ); } } finish: c.nodesVisible = lastNodesVisible; }