/* ================ R_DrawSubmodelPolygons All in one leaf ================ */ void R_DrawSubmodelPolygons(mmodel_t *pmodel, int clipflags, mnode_t *topnode) { int i; vec_t dot; mface_t *psurf; int numsurfaces; cplane_t *pplane; // FIXME: use bounding-box-based frustum clipping info? psurf = pmodel->firstface; numsurfaces = pmodel->numfaces; for (i = 0; i < numsurfaces; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = PlaneDiff(modelorg, pplane); // draw the polygon if (((psurf->drawflags & DSURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->drawflags & DSURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { r_currentkey = ((mleaf_t *)topnode)->key; // FIXME: use bounding-box-based frustum clipping info? R_RenderFace(psurf, clipflags); } } }
/* ================ R_DrawSolidClippedSubmodelPolygons Bmodel crosses multiple leafs ================ */ void R_DrawSolidClippedSubmodelPolygons(mmodel_t *pmodel, mnode_t *topnode) { int i, j; vec_t dot; mface_t *psurf; int numsurfaces; cplane_t *pplane; mvertex_t bverts[MAX_BMODEL_VERTS]; bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; msurfedge_t *surfedge; // FIXME: use bounding-box-based frustum clipping info? psurf = pmodel->firstface; numsurfaces = pmodel->numfaces; for (i = 0; i < numsurfaces; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = PlaneDiff(modelorg, pplane); // draw the polygon if ((!(psurf->drawflags & DSURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || ((psurf->drawflags & DSURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) continue; // FIXME: use bounding-box-based frustum clipping info? // copy the edges to bedges, flipping if necessary so always // clockwise winding // FIXME: if edges and vertices get caches, these assignments must move // outside the loop, and overflow checking must be done here pbverts = bverts; pbedges = bedges; numbverts = numbedges = 0; pbedge = &bedges[numbedges]; numbedges += psurf->numsurfedges; if (numbedges >= MAX_BMODEL_EDGES) { Com_Printf("Out of edges for bmodel\n"); return; } surfedge = psurf->firstsurfedge; for (j = 0; j < psurf->numsurfedges; j++, surfedge++) { pbedge[j].v[0] = surfedge->edge->v[surfedge->vert ]; pbedge[j].v[1] = surfedge->edge->v[surfedge->vert ^ 1]; pbedge[j].pnext = &pbedge[j + 1]; } pbedge[j - 1].pnext = NULL; // mark end of edges if (!(psurf->texinfo->c.flags & SURF_TRANS_MASK)) R_RecursiveClipBPoly(pbedge, topnode, psurf); else R_RenderBmodelFace(pbedge, psurf); } }
/* * R_VisCullSphere */ qboolean R_VisCullSphere( const vec3_t origin, float radius ) { float dist; int stackdepth = 0; mnode_t *node, *localstack[2048]; if( !rsh.worldModel || ( rn.refdef.rdflags & RDF_NOWORLDMODEL ) ) return qfalse; if( rn.renderFlags & RF_NOVIS ) return qfalse; radius += 4; for( node = rsh.worldBrushModel->nodes;; ) { if( node->pvsframe != rf.pvsframecount ) { if( !stackdepth ) return qtrue; node = localstack[--stackdepth]; continue; } if( !node->plane ) return qfalse; dist = PlaneDiff( origin, node->plane ); if( dist > radius ) { node = node->children[0]; continue; } else if( dist < -radius ) { node = node->children[1]; continue; } // go down both sides if( stackdepth < sizeof( localstack )/sizeof( mnode_t * ) ) localstack[stackdepth++] = node->children[0]; else assert( 0 ); node = node->children[1]; } return qtrue; }
/* =================== R_VisCullSphere =================== */ bool R_VisCullSphere( const Vector &origin, float radius ) { mnode_t *localstack[2048]; int stackdepth = 0; if( !worldmodel || !RI.drawWorld ) return false; if( r_novis->value ) return false; radius += 4; for( mnode_t *node = worldmodel->nodes; ; ) { if( node->visframe != tr.visframecount ) { if( !stackdepth ) return true; node = localstack[--stackdepth]; continue; } if( node->contents < 0 ) return false; float dist = PlaneDiff( origin, node->plane ); if( dist > radius ) { node = node->children[0]; continue; } else if( dist < -radius ) { node = node->children[1]; continue; } // go down both sides if( stackdepth < ARRAYSIZE( localstack )) localstack[stackdepth++] = node->children[0]; node = node->children[1]; } return true; }
/* * R_SurfaceShadowBits */ static unsigned int R_SurfaceShadowBits( const msurface_t *surf, unsigned int checkShadowBits ) { unsigned int i, bit; shadowGroup_t *grp; unsigned int surfShadowBits = 0; if( !R_SurfPotentiallyShadowed( surf ) ) { return 0; } for( i = 0; i < rsc.numShadowGroups; i++ ) { if( !checkShadowBits ) { break; } grp = rsc.shadowGroups + i; bit = grp->bit; if( checkShadowBits & bit ) { switch( surf->facetype ) { case FACETYPE_PLANAR: if ( BoundsIntersect( surf->mins, surf->maxs, grp->visMins, grp->visMaxs ) ) { float dist = PlaneDiff( grp->visOrigin, surf->plane ); if ( dist > -grp->visRadius && dist <= grp->visRadius ) { // crossed by plane surfShadowBits |= bit; } } break; case FACETYPE_PATCH: case FACETYPE_TRISURF: case FACETYPE_FOLIAGE: if( BoundsIntersect( surf->mins, surf->maxs, grp->visMins, grp->visMaxs ) ) { surfShadowBits |= bit; } break; } checkShadowBits &= ~bit; } } return surfShadowBits; }
/* * Mod_PointInLeaf */ mleaf_t *Mod_PointInLeaf( vec3_t p, model_t *model ) { mnode_t *node; cplane_t *plane; mbrushmodel_t *bmodel; if( !model || !( bmodel = ( mbrushmodel_t * )model->extradata ) || !bmodel->nodes ) { ri.Com_Error( ERR_DROP, "Mod_PointInLeaf: bad model" ); return NULL; } node = bmodel->nodes; do { plane = node->plane; node = node->children[PlaneDiff( p, plane ) < 0]; } while( node->plane != NULL ); return ( mleaf_t * )node; }
/* * R_SurfaceDlightBits */ static unsigned int R_SurfaceDlightBits( const msurface_t *surf, unsigned int checkDlightBits ) { unsigned int i, bit; dlight_t *lt; float dist; unsigned int surfDlightBits = 0; if( !R_SurfPotentiallyLit( surf ) ) { return 0; } for( i = 0, bit = 1, lt = rsc.dlights; i < rsc.numDlights; i++, bit <<= 1, lt++ ) { if( !checkDlightBits ) { break; } if( checkDlightBits & bit ) { switch( surf->facetype ) { case FACETYPE_PLANAR: dist = PlaneDiff( lt->origin, surf->plane ); if( dist > -lt->intensity && dist < lt->intensity ) { surfDlightBits |= bit; } break; case FACETYPE_PATCH: case FACETYPE_TRISURF: case FACETYPE_FOLIAGE: if( BoundsAndSphereIntersect( surf->mins, surf->maxs, lt->origin, lt->intensity ) ) { surfDlightBits |= bit; } break; } checkDlightBits &= ~bit; } } return surfDlightBits; }
void R_RecursiveWorldNode (mnode_t *node, int clipflags) { int i, c, side, *pindex; vec3_t acceptpt, rejectpt; mplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; double d, dot; if (node->contents == CONTENTS_SOLID) return; // solid if (node->visframe != r_visframecount) return; // 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 = DotProduct (rejectpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; 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 = DotProduct (acceptpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d >= 0) clipflags &= ~(1<<i); // node is entirely on screen } } // if a leaf node, draw stuff if (node->contents < 0) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } // deal with model fragments in this leaf if (pleaf->efrags) R_StoreEfrags (&pleaf->efrags); pleaf->key = r_currentkey; r_currentkey++; // all bmodels in a leaf share the same key } else { // 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 = PlaneDiff (modelorg, plane); side = (dot < 0); // recurse down the children, front side first R_RecursiveWorldNode (node->children[side], clipflags); // draw stuff c = node->numsurfaces; if (c) { surf = cl.worldmodel->surfaces + node->firstsurface; if (dot < -BACKFACE_EPSILON) { do { if ((surf->flags & SURF_PLANEBACK) && surf->visframe == r_framecount) R_RenderFace (surf, clipflags); surf++; } while (--c); } else if (dot > BACKFACE_EPSILON) { do { if (!(surf->flags & SURF_PLANEBACK) && surf->visframe == 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 R_RecursiveWorldNode (node->children[!side], clipflags); } }
/* ================ 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); } } } } }
void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) { int i; unsigned mask; mplane_t *pplane; float distinv; vec3_t p_normal; medge_t tedge; clipplane_t *pclip; // skip out if no more surfs if (surface_p >= surf_max) { r_outofsurfaces++; return; } // ditto if not enough edges left, or switch to auxedges if possible if ((edge_p + psurf->numedges + 4) >= edge_max) { r_outofedges += psurf->numedges; return; } c_faceclip++; // this is a dummy to give the caching mechanism someplace to write to r_pedge = &tedge; // set up clip planes pclip = NULL; for (i = 3, mask = 0x08 ; i >= 0 ; i--, mask >>= 1) { if (r_clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching can be used? r_lastvertvalid = false; for ( ; pedges ; pedges = pedges->pnext) { r_leftclipped = r_rightclipped = false; R_ClipEdge (pedges->v[0], pedges->v[1], pclip); if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_pedge = &tedge; R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_pedge = &tedge; r_nearzionly = true; R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->data = (void *)psurf; surface_p->nearzi = r_nearzi; surface_p->flags = psurf->flags; surface_p->insubmodel = true; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentbkey; surface_p->spans = NULL; pplane = psurf->plane; // FIXME: cache this? TransformVector (pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (-PlaneDiff(modelorg, pplane)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; }
static void R_TrackSprite(const entity_render_t *ent, vec3_t origin, vec3_t left, vec3_t up, int *edge, float *dir_angle) { float distance; vec3_t bCoord; // body coordinates of object unsigned int i; // temporarily abuse bCoord as the vector player->sprite-origin VectorSubtract(origin, r_refdef.view.origin, bCoord); distance = VectorLength(bCoord); // Now get the bCoords :) Matrix4x4_Transform(&r_refdef.view.inverse_matrix, origin, bCoord); *edge = 0; // FIXME::should assume edge == 0, which is correct currently for(i = 0; i < 4; ++i) { if(PlaneDiff(origin, &r_refdef.view.frustum[i]) < -EPSILON) break; } // If it wasn't outside a plane, no tracking needed if(i < 4) { float x, y; // screen X and Y coordinates float ax, ay; // absolute coords, used for division // I divide x and y by the greater absolute value to get ranges -1.0 to +1.0 bCoord[2] *= r_refdef.view.frustum_x; bCoord[1] *= r_refdef.view.frustum_y; //Con_Printf("%f %f %f\n", bCoord[0], bCoord[1], bCoord[2]); ax = fabs(bCoord[1]); ay = fabs(bCoord[2]); // get the greater value and determine the screen edge it's on if(ax < ay) { ax = ay; // 180 or 0 degrees if(bCoord[2] < 0.0f) *edge = SIDE_BOTTOM; else *edge = SIDE_TOP; } else { if(bCoord[1] < 0.0f) *edge = SIDE_RIGHT; else *edge = SIDE_LEFT; } // umm... if(ax < MIN_EPSILON) // this was == 0.0f before --blub ax = MIN_EPSILON; // get the -1 to +1 range x = bCoord[1] / ax; y = bCoord[2] / ax; ax = (1.0f / VectorLength(left)); ay = (1.0f / VectorLength(up)); // Using the placement below the distance of a sprite is // real dist = sqrt(d*d + dfxa*dfxa + dgyb*dgyb) // d is the distance we use // f is frustum X // x is x // a is ax // g is frustum Y // y is y // b is ay // real dist (r) shall be d, so // r*r = d*d + dfxa*dfxa + dgyb*dgyb // r*r = d*d * (1 + fxa*fxa + gyb*gyb) // d*d = r*r / (1 + fxa*fxa + gyb*gyb) // d = sqrt(r*r / (1 + fxa*fxa + gyb*gyb)) // thus: distance = sqrt((distance*distance) / (1.0 + r_refdef.view.frustum_x*r_refdef.view.frustum_x * x*x * ax*ax + r_refdef.view.frustum_y*r_refdef.view.frustum_y * y*y * ay*ay)); // ^ the one we want ^ the one we have ^ our factors // Place the sprite a few units ahead of the player VectorCopy(r_refdef.view.origin, origin); VectorMA(origin, distance, r_refdef.view.forward, origin); // Move the sprite left / up the screeen height VectorMA(origin, distance * r_refdef.view.frustum_x * x * ax, left, origin); VectorMA(origin, distance * r_refdef.view.frustum_y * y * ay, up, origin); if(r_track_sprites_flags.integer & TSF_ROTATE_CONTINOUSLY) { // compute the rotation, negate y axis, we're pointing outwards *dir_angle = atan(-y / x) * 180.0f/M_PI; // we need the real, full angle if(x < 0.0f) *dir_angle += 180.0f; } left[0] *= r_track_sprites_scalew.value; left[1] *= r_track_sprites_scalew.value; left[2] *= r_track_sprites_scalew.value; up[0] *= r_track_sprites_scaleh.value; up[1] *= r_track_sprites_scaleh.value; up[2] *= r_track_sprites_scaleh.value; } }
/* ================= R_CullSurface cull invisible surfaces ================= */ bool R_CullSurfaceExt( msurface_t *surf, const mplane_t frustum[6], unsigned int clipflags ) { mextrasurf_t *info; cl_entity_t *e = RI.currententity; if( !surf || !surf->texinfo || !surf->texinfo->texture ) return true; if( surf->flags & SURF_WATERCSG && !( RI.currententity->curstate.effects & EF_NOWATERCSG )) return true; if( surf->flags & SURF_NOCULL ) return false; // don't cull transparent surfaces because we should be draw decals on them if( surf->pdecals && ( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd )) return false; if( r_nocull->value ) return false; // world surfaces can be culled by vis frame too if( RI.currententity == GET_ENTITY( 0 ) && surf->visframe != tr.framecount ) return true; if( r_faceplanecull->value && glState.faceCull != 0 ) { if(!(surf->flags & SURF_DRAWTURB) || !RI.currentWaveHeight ) { if( surf->plane->normal != g_vecZero ) { float dist; if( RI.drawOrtho ) dist = surf->plane->normal[2]; else dist = PlaneDiff( tr.modelorg, surf->plane ); if( glState.faceCull == GL_FRONT || ( RI.params & RP_MIRRORVIEW )) { if( surf->flags & SURF_PLANEBACK ) { if( dist >= -BACKFACE_EPSILON ) return true; // wrong side } else { if( dist <= BACKFACE_EPSILON ) return true; // wrong side } } else if( glState.faceCull == GL_BACK ) { if( surf->flags & SURF_PLANEBACK ) { if( dist <= BACKFACE_EPSILON ) return true; // wrong side } else { if( dist >= -BACKFACE_EPSILON ) return true; // wrong side } } } } } info = SURF_INFO( surf, RI.currentmodel ); return ( clipflags && R_CullBoxExt( frustum, info->mins, info->maxs, clipflags )); }
/* * R_UpdatePortalSurface */ void R_UpdatePortalSurface( portalSurface_t *portalSurface, const mesh_t *mesh, const vec3_t mins, const vec3_t maxs, const shader_t *shader ) { unsigned int i; float dist; cplane_t plane, untransformed_plane; vec3_t v[3]; const entity_t *ent; if( !mesh || !portalSurface ) { return; } ent = portalSurface->entity; for( i = 0; i < 3; i++ ) { VectorCopy( mesh->xyzArray[mesh->elems[i]], v[i] ); } PlaneFromPoints( v, &untransformed_plane ); untransformed_plane.dist += DotProduct( ent->origin, untransformed_plane.normal ); untransformed_plane.dist += 1; // nudge along the normal a bit CategorizePlane( &untransformed_plane ); if( shader->flags & SHADER_AUTOSPRITE ) { vec3_t centre; // autosprites are quads, facing the viewer if( mesh->numVerts < 4 ) { return; } // compute centre as average of 4 vertices VectorCopy( mesh->xyzArray[mesh->elems[3]], centre ); for( i = 0; i < 3; i++ ) VectorAdd( centre, v[i], centre ); VectorMA( ent->origin, 0.25, centre, centre ); VectorNegate( &rn.viewAxis[AXIS_FORWARD], plane.normal ); plane.dist = DotProduct( plane.normal, centre ); CategorizePlane( &plane ); } else { vec3_t temp; mat3_t entity_rotation; // regular surfaces if( !Matrix3_Compare( ent->axis, axis_identity ) ) { Matrix3_Transpose( ent->axis, entity_rotation ); for( i = 0; i < 3; i++ ) { VectorCopy( v[i], temp ); Matrix3_TransformVector( entity_rotation, temp, v[i] ); VectorMA( ent->origin, ent->scale, v[i], v[i] ); } PlaneFromPoints( v, &plane ); CategorizePlane( &plane ); } else { plane = untransformed_plane; } } if( ( dist = PlaneDiff( rn.viewOrigin, &plane ) ) <= BACKFACE_EPSILON ) { // behind the portal plane if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { return; } // we need to render the backplane view } // check if portal view is opaque due to alphagen portal if( shader->portalDistance && dist > shader->portalDistance ) { return; } portalSurface->plane = plane; portalSurface->untransformed_plane = untransformed_plane; AddPointToBounds( mins, portalSurface->mins, portalSurface->maxs ); AddPointToBounds( maxs, portalSurface->mins, portalSurface->maxs ); VectorAdd( portalSurface->mins, portalSurface->maxs, portalSurface->centre ); VectorScale( portalSurface->centre, 0.5, portalSurface->centre ); }
/* * R_DrawPortalSurface * * Renders the portal view and captures the results from framebuffer if * we need to do a $portalmap stage. Note that for $portalmaps we must * use a different viewport. */ static void R_DrawPortalSurface( portalSurface_t *portalSurface ) { unsigned int i; int x, y, w, h; float dist, d, best_d; vec3_t viewerOrigin; vec3_t origin; mat3_t axis; entity_t *ent, *best; cplane_t *portal_plane = &portalSurface->plane, *untransformed_plane = &portalSurface->untransformed_plane; const shader_t *shader = portalSurface->shader; vec_t *portal_centre = portalSurface->centre; bool mirror, refraction = false; image_t *captureTexture; int captureTextureId = -1; int prevRenderFlags = 0; bool prevFlipped; bool doReflection, doRefraction; image_t *portalTexures[2] = { NULL, NULL }; doReflection = doRefraction = true; if( shader->flags & SHADER_PORTAL_CAPTURE ) { shaderpass_t *pass; captureTexture = NULL; captureTextureId = 0; for( i = 0, pass = shader->passes; i < shader->numpasses; i++, pass++ ) { if( pass->program_type == GLSL_PROGRAM_TYPE_DISTORTION ) { if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 1 ) ) { doRefraction = false; } else if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 0 ) ) { doReflection = false; } break; } } } else { captureTexture = NULL; captureTextureId = -1; } x = y = 0; w = rn.refdef.width; h = rn.refdef.height; dist = PlaneDiff( rn.viewOrigin, portal_plane ); if( dist <= BACKFACE_EPSILON || !doReflection ) { if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) || !doRefraction ) { return; } // even if we're behind the portal, we still need to capture // the second portal image for refraction refraction = true; captureTexture = NULL; captureTextureId = 1; if( dist < 0 ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist; } } mirror = true; // default to mirror view // it is stupid IMO that mirrors require a RT_PORTALSURFACE entity best = NULL; best_d = 100000000; for( i = 0; i < rn.numEntities; i++ ) { ent = R_NUM2ENT( rn.entities[i] ); if( ent->rtype != RT_PORTALSURFACE ) { continue; } d = PlaneDiff( ent->origin, untransformed_plane ); if( ( d >= -64 ) && ( d <= 64 ) ) { d = Distance( ent->origin, portal_centre ); if( d < best_d ) { best = ent; best_d = d; } } } if( best == NULL ) { if( captureTextureId < 0 ) { // still do a push&pop because to ensure the clean state if( R_PushRefInst() ) { R_PopRefInst(); } return; } } else { if( !VectorCompare( best->origin, best->origin2 ) ) { // portal mirror = false; } best->rtype = NUM_RTYPES; } prevRenderFlags = rn.renderFlags; prevFlipped = ( rn.refdef.rdflags & RDF_FLIPPED ) != 0; if( !R_PushRefInst() ) { return; } VectorCopy( rn.viewOrigin, viewerOrigin ); if( prevFlipped ) { VectorInverse( &rn.viewAxis[AXIS_RIGHT] ); } setup_and_render: if( refraction ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist; CategorizePlane( portal_plane ); VectorCopy( rn.viewOrigin, origin ); Matrix3_Copy( rn.refdef.viewaxis, axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags |= RF_PORTALVIEW; if( prevFlipped ) { rn.renderFlags |= RF_FLIPFRONTFACE; } } else if( mirror ) { VectorReflect( rn.viewOrigin, portal_plane->normal, portal_plane->dist, origin ); VectorReflect( &rn.viewAxis[AXIS_FORWARD], portal_plane->normal, 0, &axis[AXIS_FORWARD] ); VectorReflect( &rn.viewAxis[AXIS_RIGHT], portal_plane->normal, 0, &axis[AXIS_RIGHT] ); VectorReflect( &rn.viewAxis[AXIS_UP], portal_plane->normal, 0, &axis[AXIS_UP] ); Matrix3_Normalize( axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags = ( prevRenderFlags ^ RF_FLIPFRONTFACE ) | RF_MIRRORVIEW; } else { vec3_t tvec; mat3_t A, B, C, rot; // build world-to-portal rotation matrix VectorNegate( portal_plane->normal, tvec ); NormalVectorToAxis( tvec, A ); // build portal_dest-to-world rotation matrix ByteToDir( best->frame, tvec ); NormalVectorToAxis( tvec, B ); Matrix3_Transpose( B, C ); // multiply to get world-to-world rotation matrix Matrix3_Multiply( C, A, rot ); // translate view origin VectorSubtract( rn.viewOrigin, best->origin, tvec ); Matrix3_TransformVector( rot, tvec, origin ); VectorAdd( origin, best->origin2, origin ); Matrix3_Transpose( A, B ); Matrix3_Multiply( rn.viewAxis, B, rot ); Matrix3_Multiply( best->axis, rot, B ); Matrix3_Transpose( C, A ); Matrix3_Multiply( B, A, axis ); // set up portal_plane VectorCopy( &axis[AXIS_FORWARD], portal_plane->normal ); portal_plane->dist = DotProduct( best->origin2, portal_plane->normal ); CategorizePlane( portal_plane ); // for portals, vis data is taken from portal origin, not // view origin, because the view point moves around and // might fly into (or behind) a wall VectorCopy( best->origin2, rn.pvsOrigin ); VectorCopy( best->origin2, rn.lodOrigin ); rn.renderFlags |= RF_PORTALVIEW; // ignore entities, if asked politely if( best->renderfx & RF_NOPORTALENTS ) { rn.renderFlags |= RF_ENVVIEW; } if( prevFlipped ) { rn.renderFlags |= RF_FLIPFRONTFACE; } } rn.refdef.rdflags &= ~( RDF_UNDERWATER | RDF_CROSSINGWATER | RDF_FLIPPED ); rn.meshlist = &r_portallist; rn.portalmasklist = NULL; rn.renderFlags |= RF_CLIPPLANE; rn.renderFlags &= ~RF_SOFT_PARTICLES; rn.clipPlane = *portal_plane; rn.nearClip = Z_NEAR; rn.farClip = R_DefaultFarClip(); rn.polygonFactor = POLYOFFSET_FACTOR; rn.polygonUnits = POLYOFFSET_UNITS; rn.clipFlags |= 16; rn.frustum[4] = *portal_plane; // nearclip CategorizePlane( &rn.frustum[4] ); // if we want to render to a texture, initialize texture // but do not try to render to it more than once if( captureTextureId >= 0 ) { int texFlags = shader->flags & SHADER_NO_TEX_FILTERING ? IT_NOFILTERING : 0; captureTexture = R_GetPortalTexture( rsc.refdef.width, rsc.refdef.height, texFlags, rsc.frameCount ); portalTexures[captureTextureId] = captureTexture; if( !captureTexture ) { // couldn't register a slot for this plane goto done; } x = y = 0; w = captureTexture->upload_width; h = captureTexture->upload_height; rn.refdef.width = w; rn.refdef.height = h; rn.refdef.x = 0; rn.refdef.y = 0; rn.renderTarget = captureTexture->fbo; rn.renderFlags |= RF_PORTAL_CAPTURE; Vector4Set( rn.viewport, rn.refdef.x + x, rn.refdef.y + y, w, h ); Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); } else { rn.renderFlags &= ~RF_PORTAL_CAPTURE; } VectorCopy( origin, rn.refdef.vieworg ); Matrix3_Copy( axis, rn.refdef.viewaxis ); R_SetupViewMatrices( &rn.refdef ); R_SetupFrustum( &rn.refdef, rn.nearClip, rn.farClip, rn.frustum, rn.frustumCorners ); R_SetupPVS( &rn.refdef ); R_RenderView( &rn.refdef ); if( doRefraction && !refraction && ( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { rn.renderFlags = prevRenderFlags; refraction = true; captureTexture = NULL; captureTextureId = 1; goto setup_and_render; } done: portalSurface->texures[0] = portalTexures[0]; portalSurface->texures[1] = portalTexures[1]; R_PopRefInst(); }
/* =========== PointInLeaf Vic: rewrote to be faster LordHavoc: shortened a little =========== */ node_t *PointInLeaf (node_t *node, vec3_t point) { while (!node->contents) node = node->children[PlaneDiff( point, &mapplanes[node->planenum] ) <= 0]; return node; }
/* ================= R_RecursiveFragmentNode ================= */ static void R_RecursiveFragmentNode( void ) { int stackdepth = 0; float dist; bool inside; mnode_t *node, *localstack[2048]; mleaf_t *leaf; msurface_t *surf, **mark; for( node = r_worldbrushmodel->nodes, stackdepth = 0;; ) { if( node->plane == NULL ) { leaf = ( mleaf_t * )node; mark = leaf->firstFragmentSurface; if( !mark ) goto nextNodeOnStack; do { if( numFragmentVerts == maxFragmentVerts || numClippedFragments == maxClippedFragments ) return; // already reached the limit surf = *mark++; if( surf->fragmentframe == r_fragmentframecount ) continue; surf->fragmentframe = r_fragmentframecount; if( !BoundsAndSphereIntersect( surf->mins, surf->maxs, fragmentOrigin, fragmentRadius ) ) continue; if( surf->facetype == MST_PATCH ) inside = R_PatchSurfClipFragment( surf, fragmentNormal ); else inside = R_PlanarSurfClipFragment( surf, fragmentNormal ); if( inside ) return; } while( *mark ); if( numFragmentVerts == maxFragmentVerts || numClippedFragments == maxClippedFragments ) return; // already reached the limit nextNodeOnStack: if( !stackdepth ) break; node = localstack[--stackdepth]; continue; } dist = PlaneDiff( fragmentOrigin, node->plane ); if( dist > fragmentRadius ) { node = node->children[0]; continue; } if( ( dist >= -fragmentRadius ) && ( stackdepth < sizeof( localstack ) / sizeof( mnode_t * ))) localstack[stackdepth++] = node->children[0]; node = node->children[1]; } }
void R_RenderFace (msurface_t *fa, int clipflags) { int i, lindex; unsigned mask; mplane_t *pplane; float distinv; vec3_t p_normal; medge_t *pedges, tedge; clipplane_t *pclip; // skip out if no more surfs if (surface_p >= surf_max) { r_outofsurfaces++; return; } // ditto if not enough edges left, or switch to auxedges if possible if ((edge_p + fa->numedges + 4) >= edge_max) { r_outofedges += fa->numedges; return; } c_faceclip++; // set up clip planes pclip = NULL; for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { if (clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; pedges = currententity->model->edges; r_lastvertvalid = false; for (i = 0; i < fa->numedges; i++) { lindex = currententity->model->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { if ((((unsigned long)edge_p - (unsigned long)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((unsigned long) r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge (); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = false; R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], &r_pcurrentvertbase[r_pedge->v[1]], pclip); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } else { lindex = -lindex; r_pedge = &pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { // it's cached if the cached edge is valid and is owned by this medge_t if ((((unsigned long)edge_p - (unsigned long)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((unsigned long)r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge (); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = false; R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], &r_pcurrentvertbase[r_pedge->v[0]], pclip); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_pedge = &tedge; r_lastvertvalid = false; R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_pedge = &tedge; r_lastvertvalid = false; r_nearzionly = true; R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->data = (void *)fa; surface_p->nearzi = r_nearzi; surface_p->flags = fa->flags; surface_p->insubmodel = insubmodel; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentkey++; surface_p->spans = NULL; pplane = fa->plane; // FIXME: cache this? TransformVector (pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (-PlaneDiff(modelorg, pplane)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; }
/* * R_WindingClipFragment * * This function operates on windings (convex polygons without * any points inside) like triangles, quads, etc. The output is * a convex fragment (polygon, trifan) which the result of clipping * the input winding by six fragment planes. */ static qboolean R_WindingClipFragment( vec3_t *wVerts, int numVerts, msurface_t *surf, vec3_t snorm ) { int i, j; int stage, newc, numv; cplane_t *plane; qboolean front; float *v, *nextv, d; float dists[MAX_FRAGMENT_VERTS+1]; int sides[MAX_FRAGMENT_VERTS+1]; vec3_t *verts, *newverts, newv[2][MAX_FRAGMENT_VERTS], t; fragment_t *fr; numv = numVerts; verts = wVerts; for( stage = 0, plane = fragmentPlanes; stage < 6; stage++, plane++ ) { for( i = 0, v = verts[0], front = qfalse; i < numv; i++, v += 3 ) { d = PlaneDiff( v, plane ); if( d > ON_EPSILON ) { front = qtrue; sides[i] = SIDE_FRONT; } else if( d < -ON_EPSILON ) { sides[i] = SIDE_BACK; } else { front = qtrue; sides[i] = SIDE_ON; } dists[i] = d; } if( !front ) return qfalse; // clip it sides[i] = sides[0]; dists[i] = dists[0]; newc = 0; newverts = newv[stage & 1]; for( i = 0, v = verts[0]; i < numv; i++, v += 3 ) { switch( sides[i] ) { case SIDE_FRONT: if( newc == MAX_FRAGMENT_VERTS ) return qfalse; VectorCopy( v, newverts[newc] ); newc++; break; case SIDE_BACK: break; case SIDE_ON: if( newc == MAX_FRAGMENT_VERTS ) return qfalse; VectorCopy( v, newverts[newc] ); newc++; break; } if( sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) continue; if( newc == MAX_FRAGMENT_VERTS ) return qfalse; d = dists[i] / ( dists[i] - dists[i+1] ); nextv = ( i == numv - 1 ) ? verts[0] : v + 3; for( j = 0; j < 3; j++ ) newverts[newc][j] = v[j] + d * ( nextv[j] - v[j] ); newc++; } if( newc <= 2 ) return qfalse; // continue with new verts numv = newc; verts = newverts; } // fully clipped if( numFragmentVerts + numv > maxFragmentVerts ) return qfalse; fr = &clippedFragments[numClippedFragments++]; fr->numverts = numv; fr->firstvert = numFragmentVerts; fr->fognum = surf->fog ? surf->fog - rsh.worldBrushModel->fogs + 1 : -1; VectorCopy( snorm, fr->normal ); for( i = 0, v = verts[0], nextv = fragmentVerts[numFragmentVerts]; i < numv; i++, v += 3, nextv += 4 ) { VectorCopy( v, nextv ); nextv[3] = 1; } numFragmentVerts += numv; if( numFragmentVerts == maxFragmentVerts && numClippedFragments == maxClippedFragments ) return qtrue; // if all of the following is true: // a) all clipping planes are perpendicular // b) there are 4 in a clipped fragment // c) all sides of the fragment are equal (it is a quad) // d) all sides are radius*2 +- epsilon (0.001) // then it is safe to assume there's only one fragment possible // not sure if it's 100% correct, but sounds convincing if( numv == 4 ) { for( i = 0, v = verts[0]; i < numv; i++, v += 3 ) { nextv = ( i == 3 ) ? verts[0] : v + 3; VectorSubtract( v, nextv, t ); d = fragmentDiameterSquared - DotProduct( t, t ); if( d > 0.01 || d < -0.01 ) return qfalse; } return qtrue; } return qfalse; }
/* * R_RecursiveFragmentNode */ static void R_RecursiveFragmentNode( void ) { int stackdepth = 0; float dist; qboolean inside; mnode_t *node, *localstack[2048]; mleaf_t *leaf; msurface_t *surf, **mark; for( node = rsh.worldBrushModel->nodes, stackdepth = 0;; ) { if( node->plane == NULL ) { leaf = ( mleaf_t * )node; mark = leaf->firstFragmentSurface; if( !mark ) goto nextNodeOnStack; do { if( numFragmentVerts == maxFragmentVerts || numClippedFragments == maxClippedFragments ) return; // already reached the limit surf = *mark++; if( surf->fragmentframe == r_fragmentframecount ) continue; surf->fragmentframe = r_fragmentframecount; if( !BoundsAndSphereIntersect( surf->mins, surf->maxs, fragmentOrigin, fragmentRadius ) ) continue; if( surf->facetype == FACETYPE_PATCH ) inside = R_PatchSurfClipFragment( surf, fragmentNormal ); else inside = R_PlanarSurfClipFragment( surf, fragmentNormal ); // if there some fragments that are inside a surface, that doesn't mean that // there are no fragments that are OUTSIDE, so the check below is disabled //if( inside ) // return; (void)inside; // hush compiler warning } while( *mark ); if( numFragmentVerts == maxFragmentVerts || numClippedFragments == maxClippedFragments ) return; // already reached the limit nextNodeOnStack: if( !stackdepth ) break; node = localstack[--stackdepth]; continue; } dist = PlaneDiff( fragmentOrigin, node->plane ); if( dist > fragmentRadius ) { node = node->children[0]; continue; } if( ( dist >= -fragmentRadius ) && ( stackdepth < sizeof( localstack )/sizeof( mnode_t * ) ) ) localstack[stackdepth++] = node->children[0]; node = node->children[1]; } }
/* ================ R_RecursiveMirrorNode ================ */ void R_RecursiveMirrorNode( mnode_t *node, uint clipflags ) { mextrasurf_t *extrasurf; const mplane_t *clipplane; int i, clipped; msurface_t *surf, **mark; mleaf_t *pleaf; int c, side; float dot; if( node->contents == CONTENTS_SOLID ) return; // hit a solid leaf if( node->visframe != tr.visframecount ) return; if( clipflags ) { for( i = 0, clipplane = RI.frustum; i < 6; i++, clipplane++ ) { if(!( clipflags & ( 1<<i ))) continue; clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, clipplane ); if( clipped == 2 ) return; if( clipped == 1 ) clipflags &= ~(1<<i); } } // if a leaf node, draw stuff if( node->contents < 0 ) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if( c ) { do { (*mark)->visframe = tr.framecount; mark++; } while( --c ); } return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on dot = PlaneDiff( tr.modelorg, node->plane ); side = (dot >= 0) ? 0 : 1; // recurse down the children, front side first R_RecursiveMirrorNode( node->children[side], clipflags ); // draw stuff for( c = node->numsurfaces, surf = cl.worldmodel->surfaces + node->firstsurface; c; c--, surf++ ) { if(!( surf->flags & SURF_REFLECT )) continue; if( R_CullSurface( surf, clipflags )) continue; extrasurf = SURF_INFO( surf, RI.currentmodel ); extrasurf->mirrorchain = tr.mirror_entities[0].chain; tr.mirror_entities[0].chain = extrasurf; } // recurse down the back side R_RecursiveMirrorNode( node->children[!side], clipflags ); }
/* ================ 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]; } }
/* * R_DrawPortalSurface * * Renders the portal view and captures the results from framebuffer if * we need to do a $portalmap stage. Note that for $portalmaps we must * use a different viewport. */ static void R_DrawPortalSurface( portalSurface_t *portalSurface ) { unsigned int i; int x, y, w, h; float dist, d, best_d; vec3_t viewerOrigin; vec3_t origin; mat3_t axis; entity_t *ent, *best; const entity_t *portal_ent = portalSurface->entity; cplane_t *portal_plane = &portalSurface->plane, *untransformed_plane = &portalSurface->untransformed_plane; const shader_t *shader = portalSurface->shader; vec_t *portal_mins = portalSurface->mins, *portal_maxs = portalSurface->maxs; vec_t *portal_centre = portalSurface->centre; qboolean mirror, refraction = qfalse; image_t *captureTexture; int captureTextureId = -1; int prevRenderFlags = 0; qboolean doReflection, doRefraction; image_t *portalTexures[2] = { NULL, NULL }; doReflection = doRefraction = qtrue; if( shader->flags & SHADER_PORTAL_CAPTURE ) { shaderpass_t *pass; captureTexture = NULL; captureTextureId = 0; for( i = 0, pass = shader->passes; i < shader->numpasses; i++, pass++ ) { if( pass->program_type == GLSL_PROGRAM_TYPE_DISTORTION ) { if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 1 ) ) doRefraction = qfalse; else if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 0 ) ) doReflection = qfalse; break; } } } else { captureTexture = NULL; captureTextureId = -1; } x = y = 0; w = rn.refdef.width; h = rn.refdef.height; dist = PlaneDiff( rn.viewOrigin, portal_plane ); if( dist <= BACKFACE_EPSILON || !doReflection ) { if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) || !doRefraction ) return; // even if we're behind the portal, we still need to capture // the second portal image for refraction refraction = qtrue; captureTexture = NULL; captureTextureId = 1; if( dist < 0 ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist; } } if( !(rn.renderFlags & RF_NOVIS) && !R_ScissorForEntity( portal_ent, portal_mins, portal_maxs, &x, &y, &w, &h ) ) return; mirror = qtrue; // default to mirror view // it is stupid IMO that mirrors require a RT_PORTALSURFACE entity best = NULL; best_d = 100000000; for( i = 1; i < rsc.numEntities; i++ ) { ent = R_NUM2ENT(i); if( ent->rtype != RT_PORTALSURFACE ) continue; d = PlaneDiff( ent->origin, untransformed_plane ); if( ( d >= -64 ) && ( d <= 64 ) ) { d = Distance( ent->origin, portal_centre ); if( d < best_d ) { best = ent; best_d = d; } } } if( best == NULL ) { if( captureTextureId < 0 ) return; } else { if( !VectorCompare( best->origin, best->origin2 ) ) // portal mirror = qfalse; best->rtype = NUM_RTYPES; } prevRenderFlags = rn.renderFlags; if( !R_PushRefInst() ) { return; } VectorCopy( rn.viewOrigin, viewerOrigin ); setup_and_render: if( refraction ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist - 1; CategorizePlane( portal_plane ); VectorCopy( rn.viewOrigin, origin ); Matrix3_Copy( rn.refdef.viewaxis, axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags = RF_PORTALVIEW; if( !mirror ) rn.renderFlags |= RF_PVSCULL; } else if( mirror ) { VectorReflect( rn.viewOrigin, portal_plane->normal, portal_plane->dist, origin ); VectorReflect( &rn.viewAxis[AXIS_FORWARD], portal_plane->normal, 0, &axis[AXIS_FORWARD] ); VectorReflect( &rn.viewAxis[AXIS_RIGHT], portal_plane->normal, 0, &axis[AXIS_RIGHT] ); VectorReflect( &rn.viewAxis[AXIS_UP], portal_plane->normal, 0, &axis[AXIS_UP] ); Matrix3_Normalize( axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags = RF_MIRRORVIEW|RF_FLIPFRONTFACE; } else { vec3_t tvec; mat3_t A, B, C, rot; // build world-to-portal rotation matrix VectorNegate( portal_plane->normal, tvec ); NormalVectorToAxis( tvec, A ); // build portal_dest-to-world rotation matrix ByteToDir( best->frame, tvec ); NormalVectorToAxis( tvec, B ); Matrix3_Transpose( B, C ); // multiply to get world-to-world rotation matrix Matrix3_Multiply( C, A, rot ); // translate view origin VectorSubtract( rn.viewOrigin, best->origin, tvec ); Matrix3_TransformVector( rot, tvec, origin ); VectorAdd( origin, best->origin2, origin ); Matrix3_Transpose( A, B ); Matrix3_Multiply( rn.viewAxis, B, rot ); Matrix3_Multiply( best->axis, rot, B ); Matrix3_Transpose( C, A ); Matrix3_Multiply( B, A, axis ); // set up portal_plane VectorCopy( &axis[AXIS_FORWARD], portal_plane->normal ); portal_plane->dist = DotProduct( best->origin2, portal_plane->normal ); CategorizePlane( portal_plane ); // for portals, vis data is taken from portal origin, not // view origin, because the view point moves around and // might fly into (or behind) a wall rn.renderFlags = RF_PORTALVIEW|RF_PVSCULL; VectorCopy( best->origin2, rn.pvsOrigin ); VectorCopy( best->origin2, rn.lodOrigin ); // ignore entities, if asked politely if( best->renderfx & RF_NOPORTALENTS ) rn.renderFlags |= RF_NOENTS; } rn.renderFlags |= (prevRenderFlags & RF_SOFT_PARTICLES); rn.refdef.rdflags &= ~( RDF_UNDERWATER|RDF_CROSSINGWATER ); rn.shadowGroup = NULL; rn.meshlist = &r_portallist; rn.renderFlags |= RF_CLIPPLANE; rn.clipPlane = *portal_plane; rn.farClip = R_DefaultFarClip(); rn.clipFlags |= ( 1<<5 ); rn.frustum[5] = *portal_plane; CategorizePlane( &rn.frustum[5] ); // if we want to render to a texture, initialize texture // but do not try to render to it more than once if( captureTextureId >= 0 ) { int texFlags = shader->flags & SHADER_NO_TEX_FILTERING ? IT_NOFILTERING : 0; captureTexture = R_GetPortalTexture( rsc.refdef.width, rsc.refdef.height, texFlags, rsc.frameCount ); portalTexures[captureTextureId] = captureTexture; if( !captureTexture ) { // couldn't register a slot for this plane goto done; } x = y = 0; w = captureTexture->upload_width; h = captureTexture->upload_height; rn.refdef.width = w; rn.refdef.height = h; rn.refdef.x = 0; rn.refdef.y = 0; rn.fbColorAttachment = captureTexture; // no point in capturing the depth buffer due to oblique frustum messing up // the far plane and depth values rn.fbDepthAttachment = NULL; Vector4Set( rn.viewport, rn.refdef.x + x, rn.refdef.y + y, w, h ); Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); } else { // no point in capturing the depth buffer due to oblique frustum messing up // the far plane and depth values rn.fbDepthAttachment = NULL; Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); } VectorCopy( origin, rn.refdef.vieworg ); Matrix3_Copy( axis, rn.refdef.viewaxis ); R_RenderView( &rn.refdef ); if( doRefraction && !refraction && ( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { refraction = qtrue; captureTexture = NULL; captureTextureId = 1; goto setup_and_render; } done: portalSurface->texures[0] = portalTexures[0]; portalSurface->texures[1] = portalTexures[1]; R_PopRefInst( rn.fbDepthAttachment != NULL ? 0 : GL_DEPTH_BUFFER_BIT ); }
/* * R_RecursiveWorldNode */ static void R_RecursiveWorldNode( mnode_t *node, unsigned int clipFlags, unsigned int dlightBits, unsigned int shadowBits ) { unsigned int i; unsigned int dlightBits1; unsigned int shadowBits1; unsigned int bit; const cplane_t *clipplane; mleaf_t *pleaf; while( 1 ) { if( node->pvsframe != rf.pvsframecount ) return; if( clipFlags ) { for( i = sizeof( rn.frustum )/sizeof( rn.frustum[0] ), bit = 1, clipplane = rn.frustum; i > 0; i--, bit<<=1, clipplane++ ) { if( clipFlags & bit ) { int clipped = BoxOnPlaneSide( node->mins, node->maxs, clipplane ); if( clipped == 2 ) return; else if( clipped == 1 ) clipFlags &= ~bit; // node is entirely on screen } } } if( !node->plane ) break; dlightBits1 = 0; if( dlightBits ) { float dist; unsigned int checkBits = dlightBits; for( i = 0, bit = 1; i < rsc.numDlights; i++, bit <<= 1 ) { dlight_t *dl = rsc.dlights + i; if( dlightBits & bit ) { dist = PlaneDiff( dl->origin, node->plane ); if( dist < -dl->intensity ) dlightBits &= ~bit; if( dist < dl->intensity ) dlightBits1 |= bit; checkBits &= ~bit; if( !checkBits ) break; } } } shadowBits1 = 0; if( shadowBits ) { unsigned int checkBits = shadowBits; for( i = 0; i < rsc.numShadowGroups; i++ ) { shadowGroup_t *group = rsc.shadowGroups + i; bit = group->bit; if( checkBits & bit ) { int clipped = BOX_ON_PLANE_SIDE( group->visMins, group->visMaxs, node->plane ); if( !(clipped & 1) ) shadowBits &= ~bit; if( clipped & 2 ) shadowBits1 |= bit; checkBits &= ~bit; if( !checkBits ) break; } } } R_RecursiveWorldNode( node->children[0], clipFlags, dlightBits, shadowBits ); node = node->children[1]; dlightBits = dlightBits1; shadowBits = shadowBits1; } // if a leaf node, draw stuff pleaf = ( mleaf_t * )node; pleaf->visframe = rf.frameCount; // add leaf bounds to view bounds for( i = 0; i < 3; i++ ) { rn.visMins[i] = min( rn.visMins[i], pleaf->mins[i] ); rn.visMaxs[i] = max( rn.visMaxs[i], pleaf->maxs[i] ); } rn.dlightBits |= dlightBits; rn.shadowBits |= shadowBits; R_MarkLeafSurfaces( pleaf->firstVisSurface, clipFlags, dlightBits, shadowBits ); rf.stats.c_world_leafs++; }
/* * R_AddPortalSurface */ portalSurface_t *R_AddPortalSurface( const entity_t *ent, const mesh_t *mesh, const vec3_t mins, const vec3_t maxs, const shader_t *shader ) { unsigned int i; float dist; cplane_t plane, untransformed_plane; vec3_t v[3]; portalSurface_t *portalSurface; if( !mesh ) { return NULL; } if( R_FASTSKY() && !( shader->flags & (SHADER_PORTAL_CAPTURE|SHADER_PORTAL_CAPTURE2) ) ) { // r_fastsky doesn't affect portalmaps return NULL; } for( i = 0; i < 3; i++ ) { VectorCopy( mesh->xyzArray[mesh->elems[i]], v[i] ); } PlaneFromPoints( v, &untransformed_plane ); untransformed_plane.dist += DotProduct( ent->origin, untransformed_plane.normal ); CategorizePlane( &untransformed_plane ); if( shader->flags & SHADER_AUTOSPRITE ) { vec3_t centre; // autosprites are quads, facing the viewer if( mesh->numVerts < 4 ) { return NULL; } // compute centre as average of 4 vertices VectorCopy( mesh->xyzArray[mesh->elems[3]], centre ); for( i = 0; i < 3; i++ ) VectorAdd( centre, v[i], centre ); VectorMA( ent->origin, 0.25, centre, centre ); VectorNegate( &rn.viewAxis[AXIS_FORWARD], plane.normal ); plane.dist = DotProduct( plane.normal, centre ); CategorizePlane( &plane ); } else { vec3_t temp; mat3_t entity_rotation; // regular surfaces if( !Matrix3_Compare( ent->axis, axis_identity ) ) { Matrix3_Transpose( ent->axis, entity_rotation ); for( i = 0; i < 3; i++ ) { VectorCopy( v[i], temp ); Matrix3_TransformVector( entity_rotation, temp, v[i] ); VectorMA( ent->origin, ent->scale, v[i], v[i] ); } PlaneFromPoints( v, &plane ); CategorizePlane( &plane ); } else { plane = untransformed_plane; } } if( ( dist = PlaneDiff( rn.viewOrigin, &plane ) ) <= BACKFACE_EPSILON ) { // behind the portal plane if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { return NULL; } // we need to render the backplane view } // check if portal view is opaque due to alphagen portal if( shader->portalDistance && dist > shader->portalDistance ) { return NULL; } // find the matching portal plane for( i = 0; i < rn.numPortalSurfaces; i++ ) { portalSurface = &rn.portalSurfaces[i]; if( portalSurface->entity == ent && portalSurface->shader == shader && DotProduct( portalSurface->plane.normal, plane.normal ) > 0.99f && fabs( portalSurface->plane.dist - plane.dist ) < 0.1f ) { goto addsurface; } } if( i == MAX_PORTAL_SURFACES ) { // not enough space return NULL; } portalSurface = &rn.portalSurfaces[rn.numPortalSurfaces++]; portalSurface->entity = ent; portalSurface->plane = plane; portalSurface->shader = shader; portalSurface->untransformed_plane = untransformed_plane; ClearBounds( portalSurface->mins, portalSurface->maxs ); memset( portalSurface->texures, 0, sizeof( portalSurface->texures ) ); addsurface: AddPointToBounds( mins, portalSurface->mins, portalSurface->maxs ); AddPointToBounds( maxs, portalSurface->mins, portalSurface->maxs ); VectorAdd( portalSurface->mins, portalSurface->maxs, portalSurface->centre ); VectorScale( portalSurface->centre, 0.5, portalSurface->centre ); return portalSurface; }
/* * R_RenderMeshGLSL_Distortion */ static void R_RenderMeshGLSL_Distortion( r_glslfeat_t programFeatures ) { int i, last_slot; unsigned last_framenum; int state, tcgen; int width = 1, height = 1; int program, object; mat4x4_t unused; cplane_t plane; const char *key; shaderpass_t *pass = r_back.accumPasses[0]; image_t *portaltexture[2]; qboolean frontPlane; PlaneFromPoints( r_back.r_triangle0Copy, &plane ); plane.dist += DotProduct( ri.currententity->origin, plane.normal ); key = R_PortalKeyForPlane( &plane ); last_framenum = last_slot = 0; for( i = 0; i < 2; i++ ) { int slot; portaltexture[i] = NULL; slot = R_FindPortalTextureSlot( key, i+1 ); if( slot ) portaltexture[i] = r_portaltextures[slot-1]; if( portaltexture[i] == NULL ) { portaltexture[i] = r_blacktexture; } else { width = portaltexture[i]->upload_width; height = portaltexture[i]->upload_height; } // find the most recently updated texture if( portaltexture[i]->framenum > last_framenum ) { last_slot = i; last_framenum = i; } } // if textures were not updated sequentially, use the most recent one // and reset the remaining to black if( portaltexture[0]->framenum+1 != portaltexture[1]->framenum ) portaltexture[(last_slot+1)&1] = r_blacktexture; if( pass->anim_frames[0] != r_blankbumptexture ) programFeatures |= GLSL_DISTORTION_APPLY_DUDV; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; if( pass->flags & SHADERPASS_GRAYSCALE ) programFeatures |= GLSL_COMMON_APPLY_GRAYSCALE; if( portaltexture[0] != r_blacktexture ) programFeatures |= GLSL_DISTORTION_APPLY_REFLECTION; if( portaltexture[1] != r_blacktexture ) programFeatures |= GLSL_DISTORTION_APPLY_REFRACTION; frontPlane = (PlaneDiff( ri.viewOrigin, &ri.portalPlane ) > 0 ? qtrue : qfalse); if( frontPlane ) { if( pass->alphagen.type != ALPHA_GEN_IDENTITY ) programFeatures |= GLSL_DISTORTION_APPLY_DISTORTION_ALPHA; } tcgen = pass->tcgen; // store the original tcgen R_BindShaderpass( pass, pass->anim_frames[0], 0, NULL ); // dudvmap // calculate the fragment color R_ModifyColor( pass, programFeatures & GLSL_DISTORTION_APPLY_DISTORTION_ALPHA ? qtrue : qfalse, qfalse ); GL_TexEnv( GL_MODULATE ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); if( pass->anim_frames[1] ) { // eyeDot programFeatures |= GLSL_DISTORTION_APPLY_EYEDOT; pass->tcgen = TC_GEN_SVECTORS; GL_Bind( 1, pass->anim_frames[1] ); // normalmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, 1, unused, NULL ); } GL_Bind( 2, portaltexture[0] ); // reflection GL_Bind( 3, portaltexture[1] ); // refraction pass->tcgen = tcgen; // restore original tcgen // update uniforms program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, vec3_origin, NULL, NULL, NULL, frontPlane, width, height, 0, 0, 0, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); R_FlushArrays(); qglUseProgramObjectARB( 0 ); } }
void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) { int i, j, lindex, numsurfaces; vec_t dot; msurface_t *psurf; mplane_t *pplane; mvertex_t bverts[MAX_BMODEL_VERTS]; bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; medge_t *pedge, *pedges; // FIXME: use bounding-box-based frustum clipping info? psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; numsurfaces = pmodel->nummodelsurfaces; pedges = pmodel->edges; for (i = 0 ; i < numsurfaces; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = PlaneDiff (modelorg, pplane); // draw the polygon if (((psurf->flags & SURF_PLANEBACK) && dot < -BACKFACE_EPSILON) || (!(psurf->flags & SURF_PLANEBACK) && dot > BACKFACE_EPSILON)) { // FIXME: use bounding-box-based frustum clipping info? // copy the edges to bedges, flipping if necessary so always clockwise winding // FIXME: if edges and vertices get caches, these assignments must move // outside the loop, and overflow checking must be done here pbverts = bverts; pbedges = bedges; numbverts = numbedges = 0; if (psurf->numedges > 0) { pbedge = &bedges[numbedges]; numbedges += psurf->numedges; for (j = 0; j < psurf->numedges; j++) { lindex = pmodel->surfedges[psurf->firstedge + j]; if (lindex > 0) { pedge = &pedges[lindex]; pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; } else { lindex = -lindex; pedge = &pedges[lindex]; pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; } pbedge[j].pnext = &pbedge[j + 1]; } pbedge[j-1].pnext = NULL; // mark end of edges R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); } else { Sys_Error ("no edges in bmodel"); } } } }