Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

r_rast.c (19132B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // r_rast.c
     21 
     22 #include <assert.h>
     23 
     24 #include "r_local.h"
     25 
     26 #define MAXLEFTCLIPEDGES		100
     27 
     28 // !!! if these are changed, they must be changed in asm_draw.h too !!!
     29 #define FULLY_CLIPPED_CACHED	0x80000000
     30 #define FRAMECOUNT_MASK			0x7FFFFFFF
     31 
     32 unsigned int	cacheoffset;
     33 
     34 int			c_faceclip;					// number of faces clipped
     35 
     36 
     37 clipplane_t	*entity_clipplanes;
     38 clipplane_t	view_clipplanes[4];
     39 clipplane_t	world_clipplanes[16];
     40 
     41 medge_t			*r_pedge;
     42 
     43 qboolean		r_leftclipped, r_rightclipped;
     44 static qboolean	makeleftedge, makerightedge;
     45 qboolean		r_nearzionly;
     46 
     47 int		sintable[1280];
     48 int		intsintable[1280];
     49 int		blanktable[1280];		// PGM
     50 
     51 mvertex_t	r_leftenter, r_leftexit;
     52 mvertex_t	r_rightenter, r_rightexit;
     53 
     54 typedef struct
     55 {
     56 	float	u,v;
     57 	int		ceilv;
     58 } evert_t;
     59 
     60 int				r_emitted;
     61 float			r_nearzi;
     62 float			r_u1, r_v1, r_lzi1;
     63 int				r_ceilv1;
     64 
     65 qboolean		r_lastvertvalid;
     66 int				r_skyframe;
     67 
     68 msurface_t		*r_skyfaces;
     69 mplane_t		r_skyplanes[6];
     70 mtexinfo_t		r_skytexinfo[6];
     71 mvertex_t		*r_skyverts;
     72 medge_t			*r_skyedges;
     73 int				*r_skysurfedges;
     74 
     75 // I just copied this data from a box map...
     76 int skybox_planes[12] = {2,-128, 0,-128, 2,128, 1,128, 0,128, 1,-128};
     77 
     78 int box_surfedges[24] = { 1,2,3,4,  -1,5,6,7,  8,9,-6,10,  -2,-7,-9,11,
     79   12,-3,-11,-8,  -12,-10,-5,-4};
     80 int box_edges[24] = { 1,2, 2,3, 3,4, 4,1, 1,5, 5,6, 6,2, 7,8, 8,6, 5,7, 8,3, 7,4};
     81 
     82 int	box_faces[6] = {0,0,2,2,2,0};
     83 
     84 vec3_t	box_vecs[6][2] = {
     85 	{	{0,-1,0}, {-1,0,0} },
     86 	{ {0,1,0}, {0,0,-1} },
     87 	{	{0,-1,0}, {1,0,0} },
     88 	{ {1,0,0}, {0,0,-1} },
     89 	{ {0,-1,0}, {0,0,-1} },
     90 	{ {-1,0,0}, {0,0,-1} }
     91 };
     92 
     93 float	box_verts[8][3] = {
     94 	{-1,-1,-1},
     95 	{-1,1,-1},
     96 	{1,1,-1},
     97 	{1,-1,-1},
     98 	{-1,-1,1},
     99 	{-1,1,1},
    100 	{1,-1,1},
    101 	{1,1,1}
    102 };
    103 
    104 // down, west, up, north, east, south
    105 // {"rt", "bk", "lf", "ft", "up", "dn"};
    106 
    107 /*
    108 ================
    109 R_InitSkyBox
    110 
    111 ================
    112 */
    113 void R_InitSkyBox (void)
    114 {
    115 	int		i;
    116 	extern model_t *loadmodel;
    117 
    118 	r_skyfaces = loadmodel->surfaces + loadmodel->numsurfaces;
    119 	loadmodel->numsurfaces += 6;
    120 	r_skyverts = loadmodel->vertexes + loadmodel->numvertexes;
    121 	loadmodel->numvertexes += 8;
    122 	r_skyedges = loadmodel->edges + loadmodel->numedges;
    123 	loadmodel->numedges += 12;
    124 	r_skysurfedges = loadmodel->surfedges + loadmodel->numsurfedges;
    125 	loadmodel->numsurfedges += 24;
    126 	if (loadmodel->numsurfaces > MAX_MAP_FACES
    127 		|| loadmodel->numvertexes > MAX_MAP_VERTS
    128 		|| loadmodel->numedges > MAX_MAP_EDGES)
    129 		ri.Sys_Error (ERR_DROP, "InitSkyBox: map overflow");
    130 
    131 	memset (r_skyfaces, 0, 6*sizeof(*r_skyfaces));
    132 	for (i=0 ; i<6 ; i++)
    133 	{
    134 		r_skyplanes[i].normal[skybox_planes[i*2]] = 1;
    135 		r_skyplanes[i].dist = skybox_planes[i*2+1];
    136 
    137 		VectorCopy (box_vecs[i][0], r_skytexinfo[i].vecs[0]);
    138 		VectorCopy (box_vecs[i][1], r_skytexinfo[i].vecs[1]);
    139 
    140 		r_skyfaces[i].plane = &r_skyplanes[i];
    141 		r_skyfaces[i].numedges = 4;
    142 		r_skyfaces[i].flags = box_faces[i] | SURF_DRAWSKYBOX;
    143 		r_skyfaces[i].firstedge = loadmodel->numsurfedges-24+i*4;
    144 		r_skyfaces[i].texinfo = &r_skytexinfo[i];
    145 		r_skyfaces[i].texturemins[0] = -128;
    146 		r_skyfaces[i].texturemins[1] = -128;
    147 		r_skyfaces[i].extents[0] = 256;
    148 		r_skyfaces[i].extents[1] = 256;
    149 	}
    150 
    151 	for (i=0 ; i<24 ; i++)
    152 		if (box_surfedges[i] > 0)
    153 			r_skysurfedges[i] = loadmodel->numedges-13 + box_surfedges[i];
    154 		else
    155 			r_skysurfedges[i] = - (loadmodel->numedges-13 + -box_surfedges[i]);
    156 
    157 	for(i=0 ; i<12 ; i++)
    158 	{
    159 		r_skyedges[i].v[0] = loadmodel->numvertexes-9+box_edges[i*2+0];
    160 		r_skyedges[i].v[1] = loadmodel->numvertexes-9+box_edges[i*2+1];
    161 		r_skyedges[i].cachededgeoffset = 0;
    162 	}
    163 }
    164 
    165 /*
    166 ================
    167 R_EmitSkyBox
    168 ================
    169 */
    170 void R_EmitSkyBox (void)
    171 {
    172 	int		i, j;
    173 	int		oldkey;
    174 
    175 	if (insubmodel)
    176 		return;		// submodels should never have skies
    177 	if (r_skyframe == r_framecount)
    178 		return;		// already set this frame
    179 
    180 	r_skyframe = r_framecount;
    181 
    182 	// set the eight fake vertexes
    183 	for (i=0 ; i<8 ; i++)
    184 		for (j=0 ; j<3 ; j++)
    185 			r_skyverts[i].position[j] = r_origin[j] + box_verts[i][j]*128;
    186 
    187 	// set the six fake planes
    188 	for (i=0 ; i<6 ; i++)
    189 		if (skybox_planes[i*2+1] > 0)
    190 			r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]+128;
    191 		else
    192 			r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]-128;
    193 
    194 	// fix texture offseets
    195 	for (i=0 ; i<6 ; i++)
    196 	{
    197 		r_skytexinfo[i].vecs[0][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[0]);
    198 		r_skytexinfo[i].vecs[1][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[1]);
    199 	}
    200 
    201 	// emit the six faces
    202 	oldkey = r_currentkey;
    203 	r_currentkey = 0x7ffffff0;
    204  	for (i=0 ; i<6 ; i++)
    205 	{
    206 		R_RenderFace (r_skyfaces + i, 15);
    207 	}
    208 	r_currentkey = oldkey;		// bsp sorting order
    209 }
    210 
    211 
    212 #if	!id386
    213 
    214 /*
    215 ================
    216 R_EmitEdge
    217 ================
    218 */
    219 void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1)
    220 {
    221 	edge_t	*edge, *pcheck;
    222 	int		u_check;
    223 	float	u, u_step;
    224 	vec3_t	local, transformed;
    225 	float	*world;
    226 	int		v, v2, ceilv0;
    227 	float	scale, lzi0, u0, v0;
    228 	int		side;
    229 
    230 	if (r_lastvertvalid)
    231 	{
    232 		u0 = r_u1;
    233 		v0 = r_v1;
    234 		lzi0 = r_lzi1;
    235 		ceilv0 = r_ceilv1;
    236 	}
    237 	else
    238 	{
    239 		world = &pv0->position[0];
    240 	
    241 	// transform and project
    242 		VectorSubtract (world, modelorg, local);
    243 		TransformVector (local, transformed);
    244 	
    245 		if (transformed[2] < NEAR_CLIP)
    246 			transformed[2] = NEAR_CLIP;
    247 	
    248 		lzi0 = 1.0 / transformed[2];
    249 	
    250 	// FIXME: build x/yscale into transform?
    251 		scale = xscale * lzi0;
    252 		u0 = (xcenter + scale*transformed[0]);
    253 		if (u0 < r_refdef.fvrectx_adj)
    254 			u0 = r_refdef.fvrectx_adj;
    255 		if (u0 > r_refdef.fvrectright_adj)
    256 			u0 = r_refdef.fvrectright_adj;
    257 	
    258 		scale = yscale * lzi0;
    259 		v0 = (ycenter - scale*transformed[1]);
    260 		if (v0 < r_refdef.fvrecty_adj)
    261 			v0 = r_refdef.fvrecty_adj;
    262 		if (v0 > r_refdef.fvrectbottom_adj)
    263 			v0 = r_refdef.fvrectbottom_adj;
    264 	
    265 		ceilv0 = (int) ceil(v0);
    266 	}
    267 
    268 	world = &pv1->position[0];
    269 
    270 // transform and project
    271 	VectorSubtract (world, modelorg, local);
    272 	TransformVector (local, transformed);
    273 
    274 	if (transformed[2] < NEAR_CLIP)
    275 		transformed[2] = NEAR_CLIP;
    276 
    277 	r_lzi1 = 1.0 / transformed[2];
    278 
    279 	scale = xscale * r_lzi1;
    280 	r_u1 = (xcenter + scale*transformed[0]);
    281 	if (r_u1 < r_refdef.fvrectx_adj)
    282 		r_u1 = r_refdef.fvrectx_adj;
    283 	if (r_u1 > r_refdef.fvrectright_adj)
    284 		r_u1 = r_refdef.fvrectright_adj;
    285 
    286 	scale = yscale * r_lzi1;
    287 	r_v1 = (ycenter - scale*transformed[1]);
    288 	if (r_v1 < r_refdef.fvrecty_adj)
    289 		r_v1 = r_refdef.fvrecty_adj;
    290 	if (r_v1 > r_refdef.fvrectbottom_adj)
    291 		r_v1 = r_refdef.fvrectbottom_adj;
    292 
    293 	if (r_lzi1 > lzi0)
    294 		lzi0 = r_lzi1;
    295 
    296 	if (lzi0 > r_nearzi)	// for mipmap finding
    297 		r_nearzi = lzi0;
    298 
    299 // for right edges, all we want is the effect on 1/z
    300 	if (r_nearzionly)
    301 		return;
    302 
    303 	r_emitted = 1;
    304 
    305 	r_ceilv1 = (int) ceil(r_v1);
    306 
    307 
    308 // create the edge
    309 	if (ceilv0 == r_ceilv1)
    310 	{
    311 	// we cache unclipped horizontal edges as fully clipped
    312 		if (cacheoffset != 0x7FFFFFFF)
    313 		{
    314 			cacheoffset = FULLY_CLIPPED_CACHED |
    315 					(r_framecount & FRAMECOUNT_MASK);
    316 		}
    317 
    318 		return;		// horizontal edge
    319 	}
    320 
    321 	side = ceilv0 > r_ceilv1;
    322 
    323 	edge = edge_p++;
    324 
    325 	edge->owner = r_pedge;
    326 
    327 	edge->nearzi = lzi0;
    328 
    329 	if (side == 0)
    330 	{
    331 	// trailing edge (go from p1 to p2)
    332 		v = ceilv0;
    333 		v2 = r_ceilv1 - 1;
    334 
    335 		edge->surfs[0] = surface_p - surfaces;
    336 		edge->surfs[1] = 0;
    337 
    338 		u_step = ((r_u1 - u0) / (r_v1 - v0));
    339 		u = u0 + ((float)v - v0) * u_step;
    340 	}
    341 	else
    342 	{
    343 	// leading edge (go from p2 to p1)
    344 		v2 = ceilv0 - 1;
    345 		v = r_ceilv1;
    346 
    347 		edge->surfs[0] = 0;
    348 		edge->surfs[1] = surface_p - surfaces;
    349 
    350 		u_step = ((u0 - r_u1) / (v0 - r_v1));
    351 		u = r_u1 + ((float)v - r_v1) * u_step;
    352 	}
    353 
    354 	edge->u_step = u_step*0x100000;
    355 	edge->u = u*0x100000 + 0xFFFFF;
    356 
    357 // we need to do this to avoid stepping off the edges if a very nearly
    358 // horizontal edge is less than epsilon above a scan, and numeric error causes
    359 // it to incorrectly extend to the scan, and the extension of the line goes off
    360 // the edge of the screen
    361 // FIXME: is this actually needed?
    362 	if (edge->u < r_refdef.vrect_x_adj_shift20)
    363 		edge->u = r_refdef.vrect_x_adj_shift20;
    364 	if (edge->u > r_refdef.vrectright_adj_shift20)
    365 		edge->u = r_refdef.vrectright_adj_shift20;
    366 
    367 //
    368 // sort the edge in normally
    369 //
    370 	u_check = edge->u;
    371 	if (edge->surfs[0])
    372 		u_check++;	// sort trailers after leaders
    373 
    374 	if (!newedges[v] || newedges[v]->u >= u_check)
    375 	{
    376 		edge->next = newedges[v];
    377 		newedges[v] = edge;
    378 	}
    379 	else
    380 	{
    381 		pcheck = newedges[v];
    382 		while (pcheck->next && pcheck->next->u < u_check)
    383 			pcheck = pcheck->next;
    384 		edge->next = pcheck->next;
    385 		pcheck->next = edge;
    386 	}
    387 
    388 	edge->nextremove = removeedges[v2];
    389 	removeedges[v2] = edge;
    390 }
    391 
    392 
    393 /*
    394 ================
    395 R_ClipEdge
    396 ================
    397 */
    398 void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip)
    399 {
    400 	float		d0, d1, f;
    401 	mvertex_t	clipvert;
    402 
    403 	if (clip)
    404 	{
    405 		do
    406 		{
    407 			d0 = DotProduct (pv0->position, clip->normal) - clip->dist;
    408 			d1 = DotProduct (pv1->position, clip->normal) - clip->dist;
    409 
    410 			if (d0 >= 0)
    411 			{
    412 			// point 0 is unclipped
    413 				if (d1 >= 0)
    414 				{
    415 				// both points are unclipped
    416 					continue;
    417 				}
    418 
    419 			// only point 1 is clipped
    420 
    421 			// we don't cache clipped edges
    422 				cacheoffset = 0x7FFFFFFF;
    423 
    424 				f = d0 / (d0 - d1);
    425 				clipvert.position[0] = pv0->position[0] +
    426 						f * (pv1->position[0] - pv0->position[0]);
    427 				clipvert.position[1] = pv0->position[1] +
    428 						f * (pv1->position[1] - pv0->position[1]);
    429 				clipvert.position[2] = pv0->position[2] +
    430 						f * (pv1->position[2] - pv0->position[2]);
    431 
    432 				if (clip->leftedge)
    433 				{
    434 					r_leftclipped = true;
    435 					r_leftexit = clipvert;
    436 				}
    437 				else if (clip->rightedge)
    438 				{
    439 					r_rightclipped = true;
    440 					r_rightexit = clipvert;
    441 				}
    442 
    443 				R_ClipEdge (pv0, &clipvert, clip->next);
    444 				return;
    445 			}
    446 			else
    447 			{
    448 			// point 0 is clipped
    449 				if (d1 < 0)
    450 				{
    451 				// both points are clipped
    452 				// we do cache fully clipped edges
    453 					if (!r_leftclipped)
    454 						cacheoffset = FULLY_CLIPPED_CACHED |
    455 								(r_framecount & FRAMECOUNT_MASK);
    456 					return;
    457 				}
    458 
    459 			// only point 0 is clipped
    460 				r_lastvertvalid = false;
    461 
    462 			// we don't cache partially clipped edges
    463 				cacheoffset = 0x7FFFFFFF;
    464 
    465 				f = d0 / (d0 - d1);
    466 				clipvert.position[0] = pv0->position[0] +
    467 						f * (pv1->position[0] - pv0->position[0]);
    468 				clipvert.position[1] = pv0->position[1] +
    469 						f * (pv1->position[1] - pv0->position[1]);
    470 				clipvert.position[2] = pv0->position[2] +
    471 						f * (pv1->position[2] - pv0->position[2]);
    472 
    473 				if (clip->leftedge)
    474 				{
    475 					r_leftclipped = true;
    476 					r_leftenter = clipvert;
    477 				}
    478 				else if (clip->rightedge)
    479 				{
    480 					r_rightclipped = true;
    481 					r_rightenter = clipvert;
    482 				}
    483 
    484 				R_ClipEdge (&clipvert, pv1, clip->next);
    485 				return;
    486 			}
    487 		} while ((clip = clip->next) != NULL);
    488 	}
    489 
    490 // add the edge
    491 	R_EmitEdge (pv0, pv1);
    492 }
    493 
    494 #endif	// !id386
    495 
    496 
    497 /*
    498 ================
    499 R_EmitCachedEdge
    500 ================
    501 */
    502 void R_EmitCachedEdge (void)
    503 {
    504 	edge_t		*pedge_t;
    505 
    506 	pedge_t = (edge_t *)((unsigned long)r_edges + r_pedge->cachededgeoffset);
    507 
    508 	if (!pedge_t->surfs[0])
    509 		pedge_t->surfs[0] = surface_p - surfaces;
    510 	else
    511 		pedge_t->surfs[1] = surface_p - surfaces;
    512 
    513 	if (pedge_t->nearzi > r_nearzi)	// for mipmap finding
    514 		r_nearzi = pedge_t->nearzi;
    515 
    516 	r_emitted = 1;
    517 }
    518 
    519 
    520 /*
    521 ================
    522 R_RenderFace
    523 ================
    524 */
    525 void R_RenderFace (msurface_t *fa, int clipflags)
    526 {
    527 	int			i, lindex;
    528 	unsigned	mask;
    529 	mplane_t	*pplane;
    530 	float		distinv;
    531 	vec3_t		p_normal;
    532 	medge_t		*pedges, tedge;
    533 	clipplane_t	*pclip;
    534 
    535 	// translucent surfaces are not drawn by the edge renderer
    536 	if (fa->texinfo->flags & (SURF_TRANS33|SURF_TRANS66))
    537 	{
    538 		fa->nextalphasurface = r_alpha_surfaces;
    539 		r_alpha_surfaces = fa;
    540 		return;
    541 	}
    542 
    543 	// sky surfaces encountered in the world will cause the
    544 	// environment box surfaces to be emited
    545 	if ( fa->texinfo->flags & SURF_SKY )
    546 	{
    547 		R_EmitSkyBox ();	
    548 		return;
    549 	}
    550 
    551 // skip out if no more surfs
    552 	if ((surface_p) >= surf_max)
    553 	{
    554 		r_outofsurfaces++;
    555 		return;
    556 	}
    557 
    558 // ditto if not enough edges left, or switch to auxedges if possible
    559 	if ((edge_p + fa->numedges + 4) >= edge_max)
    560 	{
    561 		r_outofedges += fa->numedges;
    562 		return;
    563 	}
    564 
    565 	c_faceclip++;
    566 
    567 // set up clip planes
    568 	pclip = NULL;
    569 
    570 	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
    571 	{
    572 		if (clipflags & mask)
    573 		{
    574 			view_clipplanes[i].next = pclip;
    575 			pclip = &view_clipplanes[i];
    576 		}
    577 	}
    578 
    579 // push the edges through
    580 	r_emitted = 0;
    581 	r_nearzi = 0;
    582 	r_nearzionly = false;
    583 	makeleftedge = makerightedge = false;
    584 	pedges = currentmodel->edges;
    585 	r_lastvertvalid = false;
    586 
    587 	for (i=0 ; i<fa->numedges ; i++)
    588 	{
    589 		lindex = currentmodel->surfedges[fa->firstedge + i];
    590 
    591 		if (lindex > 0)
    592 		{
    593 			r_pedge = &pedges[lindex];
    594 
    595 		// if the edge is cached, we can just reuse the edge
    596 			if (!insubmodel)
    597 			{
    598 				if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
    599 				{
    600 					if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
    601 						r_framecount)
    602 					{
    603 						r_lastvertvalid = false;
    604 						continue;
    605 					}
    606 				}
    607 				else
    608 				{
    609 					if ((((unsigned long)edge_p - (unsigned long)r_edges) >
    610 						 r_pedge->cachededgeoffset) &&
    611 						(((edge_t *)((unsigned long)r_edges +
    612 						 r_pedge->cachededgeoffset))->owner == r_pedge))
    613 					{
    614 						R_EmitCachedEdge ();
    615 						r_lastvertvalid = false;
    616 						continue;
    617 					}
    618 				}
    619 			}
    620 
    621 		// assume it's cacheable
    622 			cacheoffset = (byte *)edge_p - (byte *)r_edges;
    623 			r_leftclipped = r_rightclipped = false;
    624 			R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]],
    625 						&r_pcurrentvertbase[r_pedge->v[1]],
    626 						pclip);
    627 			r_pedge->cachededgeoffset = cacheoffset;
    628 
    629 			if (r_leftclipped)
    630 				makeleftedge = true;
    631 			if (r_rightclipped)
    632 				makerightedge = true;
    633 			r_lastvertvalid = true;
    634 		}
    635 		else
    636 		{
    637 			lindex = -lindex;
    638 			r_pedge = &pedges[lindex];
    639 		// if the edge is cached, we can just reuse the edge
    640 			if (!insubmodel)
    641 			{
    642 				if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
    643 				{
    644 					if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
    645 						r_framecount)
    646 					{
    647 						r_lastvertvalid = false;
    648 						continue;
    649 					}
    650 				}
    651 				else
    652 				{
    653 				// it's cached if the cached edge is valid and is owned
    654 				// by this medge_t
    655 					if ((((unsigned long)edge_p - (unsigned long)r_edges) >
    656 						 r_pedge->cachededgeoffset) &&
    657 						(((edge_t *)((unsigned long)r_edges +
    658 						 r_pedge->cachededgeoffset))->owner == r_pedge))
    659 					{
    660 						R_EmitCachedEdge ();
    661 						r_lastvertvalid = false;
    662 						continue;
    663 					}
    664 				}
    665 			}
    666 
    667 		// assume it's cacheable
    668 			cacheoffset = (byte *)edge_p - (byte *)r_edges;
    669 			r_leftclipped = r_rightclipped = false;
    670 			R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]],
    671 						&r_pcurrentvertbase[r_pedge->v[0]],
    672 						pclip);
    673 			r_pedge->cachededgeoffset = cacheoffset;
    674 
    675 			if (r_leftclipped)
    676 				makeleftedge = true;
    677 			if (r_rightclipped)
    678 				makerightedge = true;
    679 			r_lastvertvalid = true;
    680 		}
    681 	}
    682 
    683 // if there was a clip off the left edge, add that edge too
    684 // FIXME: faster to do in screen space?
    685 // FIXME: share clipped edges?
    686 	if (makeleftedge)
    687 	{
    688 		r_pedge = &tedge;
    689 		r_lastvertvalid = false;
    690 		R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next);
    691 	}
    692 
    693 // if there was a clip off the right edge, get the right r_nearzi
    694 	if (makerightedge)
    695 	{
    696 		r_pedge = &tedge;
    697 		r_lastvertvalid = false;
    698 		r_nearzionly = true;
    699 		R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next);
    700 	}
    701 
    702 // if no edges made it out, return without posting the surface
    703 	if (!r_emitted)
    704 		return;
    705 
    706 	r_polycount++;
    707 
    708 	surface_p->msurf = fa;
    709 	surface_p->nearzi = r_nearzi;
    710 	surface_p->flags = fa->flags;
    711 	surface_p->insubmodel = insubmodel;
    712 	surface_p->spanstate = 0;
    713 	surface_p->entity = currententity;
    714 	surface_p->key = r_currentkey++;
    715 	surface_p->spans = NULL;
    716 
    717 	pplane = fa->plane;
    718 // FIXME: cache this?
    719 	TransformVector (pplane->normal, p_normal);
    720 // FIXME: cache this?
    721 	distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal));
    722 
    723 	surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv;
    724 	surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv;
    725 	surface_p->d_ziorigin = p_normal[2] * distinv -
    726 			xcenter * surface_p->d_zistepu -
    727 			ycenter * surface_p->d_zistepv;
    728 
    729 	surface_p++;
    730 }
    731 
    732 
    733 /*
    734 ================
    735 R_RenderBmodelFace
    736 ================
    737 */
    738 void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf)
    739 {
    740 	int			i;
    741 	unsigned	mask;
    742 	mplane_t	*pplane;
    743 	float		distinv;
    744 	vec3_t		p_normal;
    745 	medge_t		tedge;
    746 	clipplane_t	*pclip;
    747 
    748 	if (psurf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66))
    749 	{
    750 		psurf->nextalphasurface = r_alpha_surfaces;
    751 		r_alpha_surfaces = psurf;
    752 		return;
    753 	}
    754 
    755 // skip out if no more surfs
    756 	if (surface_p >= surf_max)
    757 	{
    758 		r_outofsurfaces++;
    759 		return;
    760 	}
    761 
    762 // ditto if not enough edges left, or switch to auxedges if possible
    763 	if ((edge_p + psurf->numedges + 4) >= edge_max)
    764 	{
    765 		r_outofedges += psurf->numedges;
    766 		return;
    767 	}
    768 
    769 	c_faceclip++;
    770 
    771 // this is a dummy to give the caching mechanism someplace to write to
    772 	r_pedge = &tedge;
    773 
    774 // set up clip planes
    775 	pclip = NULL;
    776 
    777 	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
    778 	{
    779 		if (r_clipflags & mask)
    780 		{
    781 			view_clipplanes[i].next = pclip;
    782 			pclip = &view_clipplanes[i];
    783 		}
    784 	}
    785 
    786 // push the edges through
    787 	r_emitted = 0;
    788 	r_nearzi = 0;
    789 	r_nearzionly = false;
    790 	makeleftedge = makerightedge = false;
    791 // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching
    792 // can be used?
    793 	r_lastvertvalid = false;
    794 
    795 	for ( ; pedges ; pedges = pedges->pnext)
    796 	{
    797 		r_leftclipped = r_rightclipped = false;
    798 		R_ClipEdge (pedges->v[0], pedges->v[1], pclip);
    799 
    800 		if (r_leftclipped)
    801 			makeleftedge = true;
    802 		if (r_rightclipped)
    803 			makerightedge = true;
    804 	}
    805 
    806 // if there was a clip off the left edge, add that edge too
    807 // FIXME: faster to do in screen space?
    808 // FIXME: share clipped edges?
    809 	if (makeleftedge)
    810 	{
    811 		r_pedge = &tedge;
    812 		R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next);
    813 	}
    814 
    815 // if there was a clip off the right edge, get the right r_nearzi
    816 	if (makerightedge)
    817 	{
    818 		r_pedge = &tedge;
    819 		r_nearzionly = true;
    820 		R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next);
    821 	}
    822 
    823 // if no edges made it out, return without posting the surface
    824 	if (!r_emitted)
    825 		return;
    826 
    827 	r_polycount++;
    828 
    829 	surface_p->msurf = psurf;
    830 	surface_p->nearzi = r_nearzi;
    831 	surface_p->flags = psurf->flags;
    832 	surface_p->insubmodel = true;
    833 	surface_p->spanstate = 0;
    834 	surface_p->entity = currententity;
    835 	surface_p->key = r_currentbkey;
    836 	surface_p->spans = NULL;
    837 
    838 	pplane = psurf->plane;
    839 // FIXME: cache this?
    840 	TransformVector (pplane->normal, p_normal);
    841 // FIXME: cache this?
    842 	distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal));
    843 
    844 	surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv;
    845 	surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv;
    846 	surface_p->d_ziorigin = p_normal[2] * distinv -
    847 			xcenter * surface_p->d_zistepu -
    848 			ycenter * surface_p->d_zistepv;
    849 
    850 	surface_p++;
    851 }
    852