1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5 
6 This file is part of Tremulous.
7 
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 // tr_main.c -- main control flow for each frame
24 
25 #include "tr_local.h"
26 
27 #include <string.h> // memcpy
28 
29 trGlobals_t		tr;
30 
31 static float	s_flipMatrix[16] = {
32 	// convert from our coordinate system (looking down X)
33 	// to OpenGL's coordinate system (looking down -Z)
34 	0, 0, -1, 0,
35 	-1, 0, 0, 0,
36 	0, 1, 0, 0,
37 	0, 0, 0, 1
38 };
39 
40 
41 refimport_t	ri;
42 
43 // entities that will have procedurally generated surfaces will just
44 // point at this for their sorting surface
45 surfaceType_t	entitySurface = SF_ENTITY;
46 
47 /*
48 =================
49 R_CullLocalBox
50 
51 Returns CULL_IN, CULL_CLIP, or CULL_OUT
52 =================
53 */
R_CullLocalBox(vec3_t bounds[2])54 int R_CullLocalBox (vec3_t bounds[2]) {
55 	int		i, j;
56 	vec3_t	transformed[8];
57 	float	dists[8];
58 	vec3_t	v;
59 	cplane_t	*frust;
60 	int			anyBack;
61 	int			front, back;
62 
63 	if ( r_nocull->integer ) {
64 		return CULL_CLIP;
65 	}
66 
67 	// transform into world space
68 	for (i = 0 ; i < 8 ; i++) {
69 		v[0] = bounds[i&1][0];
70 		v[1] = bounds[(i>>1)&1][1];
71 		v[2] = bounds[(i>>2)&1][2];
72 
73 		VectorCopy( tr.or.origin, transformed[i] );
74 		VectorMA( transformed[i], v[0], tr.or.axis[0], transformed[i] );
75 		VectorMA( transformed[i], v[1], tr.or.axis[1], transformed[i] );
76 		VectorMA( transformed[i], v[2], tr.or.axis[2], transformed[i] );
77 	}
78 
79 	// check against frustum planes
80 	anyBack = 0;
81 	for (i = 0 ; i < 4 ; i++) {
82 		frust = &tr.viewParms.frustum[i];
83 
84 		front = back = 0;
85 		for (j = 0 ; j < 8 ; j++) {
86 			dists[j] = DotProduct(transformed[j], frust->normal);
87 			if ( dists[j] > frust->dist ) {
88 				front = 1;
89 				if ( back ) {
90 					break;		// a point is in front
91 				}
92 			} else {
93 				back = 1;
94 			}
95 		}
96 		if ( !front ) {
97 			// all points were behind one of the planes
98 			return CULL_OUT;
99 		}
100 		anyBack |= back;
101 	}
102 
103 	if ( !anyBack ) {
104 		return CULL_IN;		// completely inside frustum
105 	}
106 
107 	return CULL_CLIP;		// partially clipped
108 }
109 
110 /*
111 ** R_CullLocalPointAndRadius
112 */
R_CullLocalPointAndRadius(vec3_t pt,float radius)113 int R_CullLocalPointAndRadius( vec3_t pt, float radius )
114 {
115 	vec3_t transformed;
116 
117 	R_LocalPointToWorld( pt, transformed );
118 
119 	return R_CullPointAndRadius( transformed, radius );
120 }
121 
122 /*
123 ** R_CullPointAndRadius
124 */
R_CullPointAndRadius(vec3_t pt,float radius)125 int R_CullPointAndRadius( vec3_t pt, float radius )
126 {
127 	int		i;
128 	float	dist;
129 	cplane_t	*frust;
130 	qboolean mightBeClipped = qfalse;
131 
132 	if ( r_nocull->integer ) {
133 		return CULL_CLIP;
134 	}
135 
136 	// check against frustum planes
137 	for (i = 0 ; i < 4 ; i++)
138 	{
139 		frust = &tr.viewParms.frustum[i];
140 
141 		dist = DotProduct( pt, frust->normal) - frust->dist;
142 		if ( dist < -radius )
143 		{
144 			return CULL_OUT;
145 		}
146 		else if ( dist <= radius )
147 		{
148 			mightBeClipped = qtrue;
149 		}
150 	}
151 
152 	if ( mightBeClipped )
153 	{
154 		return CULL_CLIP;
155 	}
156 
157 	return CULL_IN;		// completely inside frustum
158 }
159 
160 
161 /*
162 =================
163 R_LocalNormalToWorld
164 
165 =================
166 */
R_LocalNormalToWorld(vec3_t local,vec3_t world)167 void R_LocalNormalToWorld (vec3_t local, vec3_t world) {
168 	world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0];
169 	world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1];
170 	world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2];
171 }
172 
173 /*
174 =================
175 R_LocalPointToWorld
176 
177 =================
178 */
R_LocalPointToWorld(vec3_t local,vec3_t world)179 void R_LocalPointToWorld (vec3_t local, vec3_t world) {
180 	world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0] + tr.or.origin[0];
181 	world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1] + tr.or.origin[1];
182 	world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2] + tr.or.origin[2];
183 }
184 
185 /*
186 =================
187 R_WorldToLocal
188 
189 =================
190 */
R_WorldToLocal(vec3_t world,vec3_t local)191 void R_WorldToLocal (vec3_t world, vec3_t local) {
192 	local[0] = DotProduct(world, tr.or.axis[0]);
193 	local[1] = DotProduct(world, tr.or.axis[1]);
194 	local[2] = DotProduct(world, tr.or.axis[2]);
195 }
196 
197 /*
198 ==========================
199 R_TransformModelToClip
200 
201 ==========================
202 */
R_TransformModelToClip(const vec3_t src,const float * modelMatrix,const float * projectionMatrix,vec4_t eye,vec4_t dst)203 void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix,
204 							vec4_t eye, vec4_t dst ) {
205 	int i;
206 
207 	for ( i = 0 ; i < 4 ; i++ ) {
208 		eye[i] =
209 			src[0] * modelMatrix[ i + 0 * 4 ] +
210 			src[1] * modelMatrix[ i + 1 * 4 ] +
211 			src[2] * modelMatrix[ i + 2 * 4 ] +
212 			1 * modelMatrix[ i + 3 * 4 ];
213 	}
214 
215 	for ( i = 0 ; i < 4 ; i++ ) {
216 		dst[i] =
217 			eye[0] * projectionMatrix[ i + 0 * 4 ] +
218 			eye[1] * projectionMatrix[ i + 1 * 4 ] +
219 			eye[2] * projectionMatrix[ i + 2 * 4 ] +
220 			eye[3] * projectionMatrix[ i + 3 * 4 ];
221 	}
222 }
223 
224 /*
225 ==========================
226 R_TransformClipToWindow
227 
228 ==========================
229 */
R_TransformClipToWindow(const vec4_t clip,const viewParms_t * view,vec4_t normalized,vec4_t window)230 void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ) {
231 	normalized[0] = clip[0] / clip[3];
232 	normalized[1] = clip[1] / clip[3];
233 	normalized[2] = ( clip[2] + clip[3] ) / ( 2 * clip[3] );
234 
235 	window[0] = 0.5f * ( 1.0f + normalized[0] ) * view->viewportWidth;
236 	window[1] = 0.5f * ( 1.0f + normalized[1] ) * view->viewportHeight;
237 	window[2] = normalized[2];
238 
239 	window[0] = (int) ( window[0] + 0.5 );
240 	window[1] = (int) ( window[1] + 0.5 );
241 }
242 
243 
244 /*
245 ==========================
246 myGlMultMatrix
247 
248 ==========================
249 */
myGlMultMatrix(const float * a,const float * b,float * out)250 void myGlMultMatrix( const float *a, const float *b, float *out ) {
251 	int		i, j;
252 
253 	for ( i = 0 ; i < 4 ; i++ ) {
254 		for ( j = 0 ; j < 4 ; j++ ) {
255 			out[ i * 4 + j ] =
256 				a [ i * 4 + 0 ] * b [ 0 * 4 + j ]
257 				+ a [ i * 4 + 1 ] * b [ 1 * 4 + j ]
258 				+ a [ i * 4 + 2 ] * b [ 2 * 4 + j ]
259 				+ a [ i * 4 + 3 ] * b [ 3 * 4 + j ];
260 		}
261 	}
262 }
263 
264 /*
265 =================
266 R_RotateForEntity
267 
268 Generates an orientation for an entity and viewParms
269 Does NOT produce any GL calls
270 Called by both the front end and the back end
271 =================
272 */
R_RotateForEntity(const trRefEntity_t * ent,const viewParms_t * viewParms,orientationr_t * or)273 void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms,
274 					   orientationr_t *or ) {
275 	float	glMatrix[16];
276 	vec3_t	delta;
277 	float	axisLength;
278 
279 	if ( ent->e.reType != RT_MODEL ) {
280 		*or = viewParms->world;
281 		return;
282 	}
283 
284 	VectorCopy( ent->e.origin, or->origin );
285 
286 	VectorCopy( ent->e.axis[0], or->axis[0] );
287 	VectorCopy( ent->e.axis[1], or->axis[1] );
288 	VectorCopy( ent->e.axis[2], or->axis[2] );
289 
290 	glMatrix[0] = or->axis[0][0];
291 	glMatrix[4] = or->axis[1][0];
292 	glMatrix[8] = or->axis[2][0];
293 	glMatrix[12] = or->origin[0];
294 
295 	glMatrix[1] = or->axis[0][1];
296 	glMatrix[5] = or->axis[1][1];
297 	glMatrix[9] = or->axis[2][1];
298 	glMatrix[13] = or->origin[1];
299 
300 	glMatrix[2] = or->axis[0][2];
301 	glMatrix[6] = or->axis[1][2];
302 	glMatrix[10] = or->axis[2][2];
303 	glMatrix[14] = or->origin[2];
304 
305 	glMatrix[3] = 0;
306 	glMatrix[7] = 0;
307 	glMatrix[11] = 0;
308 	glMatrix[15] = 1;
309 
310 	myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix );
311 
312 	// calculate the viewer origin in the model's space
313 	// needed for fog, specular, and environment mapping
314 	VectorSubtract( viewParms->or.origin, or->origin, delta );
315 
316 	// compensate for scale in the axes if necessary
317 	if ( ent->e.nonNormalizedAxes ) {
318 		axisLength = VectorLength( ent->e.axis[0] );
319 		if ( !axisLength ) {
320 			axisLength = 0;
321 		} else {
322 			axisLength = 1.0f / axisLength;
323 		}
324 	} else {
325 		axisLength = 1.0f;
326 	}
327 
328 	or->viewOrigin[0] = DotProduct( delta, or->axis[0] ) * axisLength;
329 	or->viewOrigin[1] = DotProduct( delta, or->axis[1] ) * axisLength;
330 	or->viewOrigin[2] = DotProduct( delta, or->axis[2] ) * axisLength;
331 }
332 
333 /*
334 =================
335 R_RotateForViewer
336 
337 Sets up the modelview matrix for a given viewParm
338 =================
339 */
R_RotateForViewer(void)340 void R_RotateForViewer (void)
341 {
342 	float	viewerMatrix[16];
343 	vec3_t	origin;
344 
345 	Com_Memset (&tr.or, 0, sizeof(tr.or));
346 	tr.or.axis[0][0] = 1;
347 	tr.or.axis[1][1] = 1;
348 	tr.or.axis[2][2] = 1;
349 	VectorCopy (tr.viewParms.or.origin, tr.or.viewOrigin);
350 
351 	// transform by the camera placement
352 	VectorCopy( tr.viewParms.or.origin, origin );
353 
354 	viewerMatrix[0] = tr.viewParms.or.axis[0][0];
355 	viewerMatrix[4] = tr.viewParms.or.axis[0][1];
356 	viewerMatrix[8] = tr.viewParms.or.axis[0][2];
357 	viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8];
358 
359 	viewerMatrix[1] = tr.viewParms.or.axis[1][0];
360 	viewerMatrix[5] = tr.viewParms.or.axis[1][1];
361 	viewerMatrix[9] = tr.viewParms.or.axis[1][2];
362 	viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9];
363 
364 	viewerMatrix[2] = tr.viewParms.or.axis[2][0];
365 	viewerMatrix[6] = tr.viewParms.or.axis[2][1];
366 	viewerMatrix[10] = tr.viewParms.or.axis[2][2];
367 	viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10];
368 
369 	viewerMatrix[3] = 0;
370 	viewerMatrix[7] = 0;
371 	viewerMatrix[11] = 0;
372 	viewerMatrix[15] = 1;
373 
374 	// convert from our coordinate system (looking down X)
375 	// to OpenGL's coordinate system (looking down -Z)
376 	myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.or.modelMatrix );
377 
378 	tr.viewParms.world = tr.or;
379 
380 }
381 
382 /*
383 ** SetFarClip
384 */
SetFarClip(void)385 static void SetFarClip( void )
386 {
387 	float	farthestCornerDistance = 0;
388 	int		i;
389 
390 	// if not rendering the world (icons, menus, etc)
391 	// set a 2k far clip plane
392 	if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
393 		tr.viewParms.zFar = 2048;
394 		return;
395 	}
396 
397 	//
398 	// set far clipping planes dynamically
399 	//
400 	farthestCornerDistance = 0;
401 	for ( i = 0; i < 8; i++ )
402 	{
403 		vec3_t v;
404 		vec3_t vecTo;
405 		float distance;
406 
407 		if ( i & 1 )
408 		{
409 			v[0] = tr.viewParms.visBounds[0][0];
410 		}
411 		else
412 		{
413 			v[0] = tr.viewParms.visBounds[1][0];
414 		}
415 
416 		if ( i & 2 )
417 		{
418 			v[1] = tr.viewParms.visBounds[0][1];
419 		}
420 		else
421 		{
422 			v[1] = tr.viewParms.visBounds[1][1];
423 		}
424 
425 		if ( i & 4 )
426 		{
427 			v[2] = tr.viewParms.visBounds[0][2];
428 		}
429 		else
430 		{
431 			v[2] = tr.viewParms.visBounds[1][2];
432 		}
433 
434 		VectorSubtract( v, tr.viewParms.or.origin, vecTo );
435 
436 		distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2];
437 
438 		if ( distance > farthestCornerDistance )
439 		{
440 			farthestCornerDistance = distance;
441 		}
442 	}
443 	tr.viewParms.zFar = sqrt( farthestCornerDistance );
444 }
445 
446 
447 /*
448 ===============
449 R_SetupProjection
450 ===============
451 */
R_SetupProjection(void)452 void R_SetupProjection( void ) {
453 	float	xmin, xmax, ymin, ymax;
454 	float	width, height, depth;
455 	float	zNear, zFar;
456 
457 	// dynamically compute far clip plane distance
458 	SetFarClip();
459 
460 	//
461 	// set up projection matrix
462 	//
463 	zNear	= r_znear->value;
464 	zFar	= tr.viewParms.zFar;
465 
466 	ymax = zNear * tan( tr.refdef.fov_y * M_PI / 360.0f );
467 	ymin = -ymax;
468 
469 	xmax = zNear * tan( tr.refdef.fov_x * M_PI / 360.0f );
470 	xmin = -xmax;
471 
472 	width = xmax - xmin;
473 	height = ymax - ymin;
474 	depth = zFar - zNear;
475 
476 	tr.viewParms.projectionMatrix[0] = 2 * zNear / width;
477 	tr.viewParms.projectionMatrix[4] = 0;
478 	tr.viewParms.projectionMatrix[8] = ( xmax + xmin ) / width;	// normally 0
479 	tr.viewParms.projectionMatrix[12] = 0;
480 
481 	tr.viewParms.projectionMatrix[1] = 0;
482 	tr.viewParms.projectionMatrix[5] = 2 * zNear / height;
483 	tr.viewParms.projectionMatrix[9] = ( ymax + ymin ) / height;	// normally 0
484 	tr.viewParms.projectionMatrix[13] = 0;
485 
486 	tr.viewParms.projectionMatrix[2] = 0;
487 	tr.viewParms.projectionMatrix[6] = 0;
488 	tr.viewParms.projectionMatrix[10] = -( zFar + zNear ) / depth;
489 	tr.viewParms.projectionMatrix[14] = -2 * zFar * zNear / depth;
490 
491 	tr.viewParms.projectionMatrix[3] = 0;
492 	tr.viewParms.projectionMatrix[7] = 0;
493 	tr.viewParms.projectionMatrix[11] = -1;
494 	tr.viewParms.projectionMatrix[15] = 0;
495 }
496 
497 /*
498 =================
499 R_SetupFrustum
500 
501 Setup that culling frustum planes for the current view
502 =================
503 */
R_SetupFrustum(void)504 void R_SetupFrustum (void) {
505 	int		i;
506 	float	xs, xc;
507 	float	ang;
508 
509 	ang = tr.viewParms.fovX / 180 * M_PI * 0.5f;
510 	xs = sin( ang );
511 	xc = cos( ang );
512 
513 	VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[0].normal );
514 	VectorMA( tr.viewParms.frustum[0].normal, xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[0].normal );
515 
516 	VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[1].normal );
517 	VectorMA( tr.viewParms.frustum[1].normal, -xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[1].normal );
518 
519 	ang = tr.viewParms.fovY / 180 * M_PI * 0.5f;
520 	xs = sin( ang );
521 	xc = cos( ang );
522 
523 	VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[2].normal );
524 	VectorMA( tr.viewParms.frustum[2].normal, xc, tr.viewParms.or.axis[2], tr.viewParms.frustum[2].normal );
525 
526 	VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[3].normal );
527 	VectorMA( tr.viewParms.frustum[3].normal, -xc, tr.viewParms.or.axis[2], tr.viewParms.frustum[3].normal );
528 
529 	for (i=0 ; i<4 ; i++) {
530 		tr.viewParms.frustum[i].type = PLANE_NON_AXIAL;
531 		tr.viewParms.frustum[i].dist = DotProduct (tr.viewParms.or.origin, tr.viewParms.frustum[i].normal);
532 		SetPlaneSignbits( &tr.viewParms.frustum[i] );
533 	}
534 }
535 
536 
537 /*
538 =================
539 R_MirrorPoint
540 =================
541 */
R_MirrorPoint(vec3_t in,orientation_t * surface,orientation_t * camera,vec3_t out)542 void R_MirrorPoint (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) {
543 	int		i;
544 	vec3_t	local;
545 	vec3_t	transformed;
546 	float	d;
547 
548 	VectorSubtract( in, surface->origin, local );
549 
550 	VectorClear( transformed );
551 	for ( i = 0 ; i < 3 ; i++ ) {
552 		d = DotProduct(local, surface->axis[i]);
553 		VectorMA( transformed, d, camera->axis[i], transformed );
554 	}
555 
556 	VectorAdd( transformed, camera->origin, out );
557 }
558 
R_MirrorVector(vec3_t in,orientation_t * surface,orientation_t * camera,vec3_t out)559 void R_MirrorVector (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) {
560 	int		i;
561 	float	d;
562 
563 	VectorClear( out );
564 	for ( i = 0 ; i < 3 ; i++ ) {
565 		d = DotProduct(in, surface->axis[i]);
566 		VectorMA( out, d, camera->axis[i], out );
567 	}
568 }
569 
570 
571 /*
572 =============
573 R_PlaneForSurface
574 =============
575 */
R_PlaneForSurface(surfaceType_t * surfType,cplane_t * plane)576 void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) {
577 	srfTriangles_t	*tri;
578 	srfPoly_t		*poly;
579 	drawVert_t		*v1, *v2, *v3;
580 	vec4_t			plane4;
581 
582 	if (!surfType) {
583 		Com_Memset (plane, 0, sizeof(*plane));
584 		plane->normal[0] = 1;
585 		return;
586 	}
587 	switch (*surfType) {
588 	case SF_FACE:
589 		*plane = ((srfSurfaceFace_t *)surfType)->plane;
590 		return;
591 	case SF_TRIANGLES:
592 		tri = (srfTriangles_t *)surfType;
593 		v1 = tri->verts + tri->indexes[0];
594 		v2 = tri->verts + tri->indexes[1];
595 		v3 = tri->verts + tri->indexes[2];
596 		PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz );
597 		VectorCopy( plane4, plane->normal );
598 		plane->dist = plane4[3];
599 		return;
600 	case SF_POLY:
601 		poly = (srfPoly_t *)surfType;
602 		PlaneFromPoints( plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz );
603 		VectorCopy( plane4, plane->normal );
604 		plane->dist = plane4[3];
605 		return;
606 	default:
607 		Com_Memset (plane, 0, sizeof(*plane));
608 		plane->normal[0] = 1;
609 		return;
610 	}
611 }
612 
613 /*
614 =================
615 R_GetPortalOrientation
616 
617 entityNum is the entity that the portal surface is a part of, which may
618 be moving and rotating.
619 
620 Returns qtrue if it should be mirrored
621 =================
622 */
R_GetPortalOrientations(drawSurf_t * drawSurf,int entityNum,orientation_t * surface,orientation_t * camera,vec3_t pvsOrigin,qboolean * mirror)623 qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum,
624 							 orientation_t *surface, orientation_t *camera,
625 							 vec3_t pvsOrigin, qboolean *mirror ) {
626 	int			i;
627 	cplane_t	originalPlane, plane;
628 	trRefEntity_t	*e;
629 	float		d;
630 	vec3_t		transformed;
631 
632 	// create plane axis for the portal we are seeing
633 	R_PlaneForSurface( drawSurf->surface, &originalPlane );
634 
635 	// rotate the plane if necessary
636 	if ( entityNum != ENTITYNUM_WORLD ) {
637 		tr.currentEntityNum = entityNum;
638 		tr.currentEntity = &tr.refdef.entities[entityNum];
639 
640 		// get the orientation of the entity
641 		R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or );
642 
643 		// rotate the plane, but keep the non-rotated version for matching
644 		// against the portalSurface entities
645 		R_LocalNormalToWorld( originalPlane.normal, plane.normal );
646 		plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin );
647 
648 		// translate the original plane
649 		originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin );
650 	} else {
651 		plane = originalPlane;
652 	}
653 
654 	VectorCopy( plane.normal, surface->axis[0] );
655 	PerpendicularVector( surface->axis[1], surface->axis[0] );
656 	CrossProduct( surface->axis[0], surface->axis[1], surface->axis[2] );
657 
658 	// locate the portal entity closest to this plane.
659 	// origin will be the origin of the portal, origin2 will be
660 	// the origin of the camera
661 	for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) {
662 		e = &tr.refdef.entities[i];
663 		if ( e->e.reType != RT_PORTALSURFACE ) {
664 			continue;
665 		}
666 
667 		d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist;
668 		if ( d > 64 || d < -64) {
669 			continue;
670 		}
671 
672 		// get the pvsOrigin from the entity
673 		VectorCopy( e->e.oldorigin, pvsOrigin );
674 
675 		// if the entity is just a mirror, don't use as a camera point
676 		if ( e->e.oldorigin[0] == e->e.origin[0] &&
677 			e->e.oldorigin[1] == e->e.origin[1] &&
678 			e->e.oldorigin[2] == e->e.origin[2] ) {
679 			VectorScale( plane.normal, plane.dist, surface->origin );
680 			VectorCopy( surface->origin, camera->origin );
681 			VectorSubtract( vec3_origin, surface->axis[0], camera->axis[0] );
682 			VectorCopy( surface->axis[1], camera->axis[1] );
683 			VectorCopy( surface->axis[2], camera->axis[2] );
684 
685 			*mirror = qtrue;
686 			return qtrue;
687 		}
688 
689 		// project the origin onto the surface plane to get
690 		// an origin point we can rotate around
691 		d = DotProduct( e->e.origin, plane.normal ) - plane.dist;
692 		VectorMA( e->e.origin, -d, surface->axis[0], surface->origin );
693 
694 		// now get the camera origin and orientation
695 		VectorCopy( e->e.oldorigin, camera->origin );
696 		AxisCopy( e->e.axis, camera->axis );
697 		VectorSubtract( vec3_origin, camera->axis[0], camera->axis[0] );
698 		VectorSubtract( vec3_origin, camera->axis[1], camera->axis[1] );
699 
700 		// optionally rotate
701 		if ( e->e.oldframe ) {
702 			// if a speed is specified
703 			if ( e->e.frame ) {
704 				// continuous rotate
705 				d = (tr.refdef.time/1000.0f) * e->e.frame;
706 				VectorCopy( camera->axis[1], transformed );
707 				RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d );
708 				CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] );
709 			} else {
710 				// bobbing rotate, with skinNum being the rotation offset
711 				d = sin( tr.refdef.time * 0.003f );
712 				d = e->e.skinNum + d * 4;
713 				VectorCopy( camera->axis[1], transformed );
714 				RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d );
715 				CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] );
716 			}
717 		}
718 		else if ( e->e.skinNum ) {
719 			d = e->e.skinNum;
720 			VectorCopy( camera->axis[1], transformed );
721 			RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d );
722 			CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] );
723 		}
724 		*mirror = qfalse;
725 		return qtrue;
726 	}
727 
728 	// if we didn't locate a portal entity, don't render anything.
729 	// We don't want to just treat it as a mirror, because without a
730 	// portal entity the server won't have communicated a proper entity set
731 	// in the snapshot
732 
733 	// unfortunately, with local movement prediction it is easily possible
734 	// to see a surface before the server has communicated the matching
735 	// portal surface entity, so we don't want to print anything here...
736 
737 	//ri.Printf( PRINT_ALL, "Portal surface without a portal entity\n" );
738 
739 	return qfalse;
740 }
741 
IsMirror(const drawSurf_t * drawSurf,int entityNum)742 static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum )
743 {
744 	int			i;
745 	cplane_t	originalPlane, plane;
746 	trRefEntity_t	*e;
747 	float		d;
748 
749 	// create plane axis for the portal we are seeing
750 	R_PlaneForSurface( drawSurf->surface, &originalPlane );
751 
752 	// rotate the plane if necessary
753 	if ( entityNum != ENTITYNUM_WORLD )
754 	{
755 		tr.currentEntityNum = entityNum;
756 		tr.currentEntity = &tr.refdef.entities[entityNum];
757 
758 		// get the orientation of the entity
759 		R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or );
760 
761 		// rotate the plane, but keep the non-rotated version for matching
762 		// against the portalSurface entities
763 		R_LocalNormalToWorld( originalPlane.normal, plane.normal );
764 		plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin );
765 
766 		// translate the original plane
767 		originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin );
768 	}
769 	else
770 	{
771 		plane = originalPlane;
772 	}
773 
774 	// locate the portal entity closest to this plane.
775 	// origin will be the origin of the portal, origin2 will be
776 	// the origin of the camera
777 	for ( i = 0 ; i < tr.refdef.num_entities ; i++ )
778 	{
779 		e = &tr.refdef.entities[i];
780 		if ( e->e.reType != RT_PORTALSURFACE ) {
781 			continue;
782 		}
783 
784 		d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist;
785 		if ( d > 64 || d < -64) {
786 			continue;
787 		}
788 
789 		// if the entity is just a mirror, don't use as a camera point
790 		if ( e->e.oldorigin[0] == e->e.origin[0] &&
791 			e->e.oldorigin[1] == e->e.origin[1] &&
792 			e->e.oldorigin[2] == e->e.origin[2] )
793 		{
794 			return qtrue;
795 		}
796 
797 		return qfalse;
798 	}
799 	return qfalse;
800 }
801 
802 /*
803 ** SurfIsOffscreen
804 **
805 ** Determines if a surface is completely offscreen.
806 */
SurfIsOffscreen(const drawSurf_t * drawSurf,vec4_t clipDest[128])807 static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) {
808 	float shortest = 100000000;
809 	int entityNum;
810 	int numTriangles;
811 	shader_t *shader;
812 	int		fogNum;
813 	int dlighted;
814 	vec4_t clip, eye;
815 	int i;
816 	unsigned int pointOr = 0;
817 	unsigned int pointAnd = (unsigned int)~0;
818 
819 	if ( glConfig.smpActive ) {		// FIXME!  we can't do RB_BeginSurface/RB_EndSurface stuff with smp!
820 		return qfalse;
821 	}
822 
823 	R_RotateForViewer();
824 
825 	R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted );
826 	RB_BeginSurface( shader, fogNum );
827 	rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
828 
829 	assert( tess.numVertexes < 128 );
830 
831 	for ( i = 0; i < tess.numVertexes; i++ )
832 	{
833 		int j;
834 		unsigned int pointFlags = 0;
835 
836 		R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip );
837 
838 		for ( j = 0; j < 3; j++ )
839 		{
840 			if ( clip[j] >= clip[3] )
841 			{
842 				pointFlags |= (1 << (j*2));
843 			}
844 			else if ( clip[j] <= -clip[3] )
845 			{
846 				pointFlags |= ( 1 << (j*2+1));
847 			}
848 		}
849 		pointAnd &= pointFlags;
850 		pointOr |= pointFlags;
851 	}
852 
853 	// trivially reject
854 	if ( pointAnd )
855 	{
856 		return qtrue;
857 	}
858 
859 	// determine if this surface is backfaced and also determine the distance
860 	// to the nearest vertex so we can cull based on portal range.  Culling
861 	// based on vertex distance isn't 100% correct (we should be checking for
862 	// range to the surface), but it's good enough for the types of portals
863 	// we have in the game right now.
864 	numTriangles = tess.numIndexes / 3;
865 
866 	for ( i = 0; i < tess.numIndexes; i += 3 )
867 	{
868 		vec3_t normal;
869 		float dot;
870 		float len;
871 
872 		VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal );
873 
874 		len = VectorLengthSquared( normal );			// lose the sqrt
875 		if ( len < shortest )
876 		{
877 			shortest = len;
878 		}
879 
880 		if ( ( dot = DotProduct( normal, tess.normal[tess.indexes[i]] ) ) >= 0 )
881 		{
882 			numTriangles--;
883 		}
884 	}
885 	if ( !numTriangles )
886 	{
887 		return qtrue;
888 	}
889 
890 	// mirrors can early out at this point, since we don't do a fade over distance
891 	// with them (although we could)
892 	if ( IsMirror( drawSurf, entityNum ) )
893 	{
894 		return qfalse;
895 	}
896 
897 	if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) )
898 	{
899 		return qtrue;
900 	}
901 
902 	return qfalse;
903 }
904 
905 /*
906 ========================
907 R_MirrorViewBySurface
908 
909 Returns qtrue if another view has been rendered
910 ========================
911 */
R_MirrorViewBySurface(drawSurf_t * drawSurf,int entityNum)912 qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) {
913 	vec4_t			clipDest[128];
914 	viewParms_t		newParms;
915 	viewParms_t		oldParms;
916 	orientation_t	surface, camera;
917 
918 	// don't recursively mirror
919 	if (tr.viewParms.isPortal) {
920 		ri.Printf( PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n" );
921 		return qfalse;
922 	}
923 
924 	if ( r_noportals->integer || (r_fastsky->integer == 1) ) {
925 		return qfalse;
926 	}
927 
928 	// trivially reject portal/mirror
929 	if ( SurfIsOffscreen( drawSurf, clipDest ) ) {
930 		return qfalse;
931 	}
932 
933 	// save old viewParms so we can return to it after the mirror view
934 	oldParms = tr.viewParms;
935 
936 	newParms = tr.viewParms;
937 	newParms.isPortal = qtrue;
938 	if ( !R_GetPortalOrientations( drawSurf, entityNum, &surface, &camera,
939 		newParms.pvsOrigin, &newParms.isMirror ) ) {
940 		return qfalse;		// bad portal, no portalentity
941 	}
942 
943 	R_MirrorPoint (oldParms.or.origin, &surface, &camera, newParms.or.origin );
944 
945 	VectorSubtract( vec3_origin, camera.axis[0], newParms.portalPlane.normal );
946 	newParms.portalPlane.dist = DotProduct( camera.origin, newParms.portalPlane.normal );
947 
948 	R_MirrorVector (oldParms.or.axis[0], &surface, &camera, newParms.or.axis[0]);
949 	R_MirrorVector (oldParms.or.axis[1], &surface, &camera, newParms.or.axis[1]);
950 	R_MirrorVector (oldParms.or.axis[2], &surface, &camera, newParms.or.axis[2]);
951 
952 	// OPTIMIZE: restrict the viewport on the mirrored view
953 
954 	// render the mirror view
955 	R_RenderView (&newParms);
956 
957 	tr.viewParms = oldParms;
958 
959 	return qtrue;
960 }
961 
962 /*
963 =================
964 R_SpriteFogNum
965 
966 See if a sprite is inside a fog volume
967 =================
968 */
R_SpriteFogNum(trRefEntity_t * ent)969 int R_SpriteFogNum( trRefEntity_t *ent ) {
970 	int				i, j;
971 	fog_t			*fog;
972 
973 	if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
974 		return 0;
975 	}
976 
977 	for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
978 		fog = &tr.world->fogs[i];
979 		for ( j = 0 ; j < 3 ; j++ ) {
980 			if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) {
981 				break;
982 			}
983 			if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) {
984 				break;
985 			}
986 		}
987 		if ( j == 3 ) {
988 			return i;
989 		}
990 	}
991 
992 	return 0;
993 }
994 
995 /*
996 ==========================================================================================
997 
998 DRAWSURF SORTING
999 
1000 ==========================================================================================
1001 */
1002 
1003 /*
1004 ===============
1005 R_Radix
1006 ===============
1007 */
R_Radix(int byte,int size,drawSurf_t * source,drawSurf_t * dest)1008 static ID_INLINE void R_Radix( int byte, int size, drawSurf_t *source, drawSurf_t *dest )
1009 {
1010   int           count[ 256 ] = { 0 };
1011   int           index[ 256 ];
1012   int           i;
1013   unsigned char *sortKey = NULL;
1014   unsigned char *end = NULL;
1015 
1016   sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte;
1017   end = sortKey + ( size * sizeof( drawSurf_t ) );
1018   for( ; sortKey < end; sortKey += sizeof( drawSurf_t ) )
1019     ++count[ *sortKey ];
1020 
1021   index[ 0 ] = 0;
1022 
1023   for( i = 1; i < 256; ++i )
1024     index[ i ] = index[ i - 1 ] + count[ i - 1 ];
1025 
1026   sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte;
1027   for( i = 0; i < size; ++i, sortKey += sizeof( drawSurf_t ) )
1028     dest[ index[ *sortKey ]++ ] = source[ i ];
1029 }
1030 
1031 /*
1032 ===============
1033 R_RadixSort
1034 
1035 Radix sort with 4 byte size buckets
1036 ===============
1037 */
R_RadixSort(drawSurf_t * source,int size)1038 static void R_RadixSort( drawSurf_t *source, int size )
1039 {
1040   static drawSurf_t scratch[ MAX_DRAWSURFS ];
1041 
1042   R_Radix( 0, size, source, scratch );
1043   R_Radix( 1, size, scratch, source );
1044   R_Radix( 2, size, source, scratch );
1045   R_Radix( 3, size, scratch, source );
1046 }
1047 
1048 //==========================================================================================
1049 
1050 /*
1051 =================
1052 R_AddDrawSurf
1053 =================
1054 */
R_AddDrawSurf(surfaceType_t * surface,shader_t * shader,int fogIndex,int dlightMap)1055 void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader,
1056 				   int fogIndex, int dlightMap ) {
1057 	int			index;
1058 
1059 	// instead of checking for overflow, we just mask the index
1060 	// so it wraps around
1061 	index = tr.refdef.numDrawSurfs & DRAWSURF_MASK;
1062 	// the sort data is packed into a single 32 bit value so it can be
1063 	// compared quickly during the qsorting process
1064 	tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT)
1065 		| tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) | (int)dlightMap;
1066 	tr.refdef.drawSurfs[index].surface = surface;
1067 	tr.refdef.numDrawSurfs++;
1068 }
1069 
1070 /*
1071 =================
1072 R_DecomposeSort
1073 =================
1074 */
R_DecomposeSort(unsigned sort,int * entityNum,shader_t ** shader,int * fogNum,int * dlightMap)1075 void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader,
1076 					 int *fogNum, int *dlightMap ) {
1077 	*fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31;
1078 	*shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ];
1079 	*entityNum = ( sort >> QSORT_ENTITYNUM_SHIFT ) & 1023;
1080 	*dlightMap = sort & 3;
1081 }
1082 
1083 /*
1084 =================
1085 R_SortDrawSurfs
1086 =================
1087 */
R_SortDrawSurfs(drawSurf_t * drawSurfs,int numDrawSurfs)1088 void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) {
1089 	shader_t		*shader;
1090 	int				fogNum;
1091 	int				entityNum;
1092 	int				dlighted;
1093 	int				i;
1094 
1095 	// it is possible for some views to not have any surfaces
1096 	if ( numDrawSurfs < 1 ) {
1097 		// we still need to add it for hyperspace cases
1098 		R_AddDrawSurfCmd( drawSurfs, numDrawSurfs );
1099 		return;
1100 	}
1101 
1102 	// if we overflowed MAX_DRAWSURFS, the drawsurfs
1103 	// wrapped around in the buffer and we will be missing
1104 	// the first surfaces, not the last ones
1105 	if ( numDrawSurfs > MAX_DRAWSURFS ) {
1106 		numDrawSurfs = MAX_DRAWSURFS;
1107 	}
1108 
1109 	// sort the drawsurfs by sort type, then orientation, then shader
1110 	R_RadixSort( drawSurfs, numDrawSurfs );
1111 
1112 	// check for any pass through drawing, which
1113 	// may cause another view to be rendered first
1114 	for ( i = 0 ; i < numDrawSurfs ; i++ ) {
1115 		R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted );
1116 
1117 		if ( shader->sort > SS_PORTAL ) {
1118 			break;
1119 		}
1120 
1121 		// no shader should ever have this sort type
1122 		if ( shader->sort == SS_BAD ) {
1123 			ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name );
1124 		}
1125 
1126 		// if the mirror was completely clipped away, we may need to check another surface
1127 		if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) {
1128 			// this is a debug option to see exactly what is being mirrored
1129 			if ( r_portalOnly->integer ) {
1130 				return;
1131 			}
1132 			break;		// only one mirror view at a time
1133 		}
1134 	}
1135 
1136 	R_AddDrawSurfCmd( drawSurfs, numDrawSurfs );
1137 }
1138 
1139 /*
1140 =============
1141 R_AddEntitySurfaces
1142 =============
1143 */
R_AddEntitySurfaces(void)1144 void R_AddEntitySurfaces (void) {
1145 	trRefEntity_t	*ent;
1146 	shader_t		*shader;
1147 
1148 	if ( !r_drawentities->integer ) {
1149 		return;
1150 	}
1151 
1152 	for ( tr.currentEntityNum = 0;
1153 	      tr.currentEntityNum < tr.refdef.num_entities;
1154 		  tr.currentEntityNum++ ) {
1155 		ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum];
1156 
1157 		ent->needDlights = qfalse;
1158 
1159 		// preshift the value we are going to OR into the drawsurf sort
1160 		tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT;
1161 
1162 		//
1163 		// the weapon model must be handled special --
1164 		// we don't want the hacked weapon position showing in
1165 		// mirrors, because the true body position will already be drawn
1166 		//
1167 		if ( (ent->e.renderfx & RF_FIRST_PERSON) && tr.viewParms.isPortal) {
1168 			continue;
1169 		}
1170 
1171 		// simple generated models, like sprites and beams, are not culled
1172 		switch ( ent->e.reType ) {
1173 		case RT_PORTALSURFACE:
1174 			break;		// don't draw anything
1175 		case RT_SPRITE:
1176 		case RT_BEAM:
1177 		case RT_LIGHTNING:
1178 		case RT_RAIL_CORE:
1179 		case RT_RAIL_RINGS:
1180 			// self blood sprites, talk balloons, etc should not be drawn in the primary
1181 			// view.  We can't just do this check for all entities, because md3
1182 			// entities may still want to cast shadows from them
1183 			if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) {
1184 				continue;
1185 			}
1186 			shader = R_GetShaderByHandle( ent->e.customShader );
1187 			R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0 );
1188 			break;
1189 
1190 		case RT_MODEL:
1191 			// we must set up parts of tr.or for model culling
1192 			R_RotateForEntity( ent, &tr.viewParms, &tr.or );
1193 
1194 			tr.currentModel = R_GetModelByHandle( ent->e.hModel );
1195 			if (!tr.currentModel) {
1196 				R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 );
1197 			} else {
1198 				switch ( tr.currentModel->type ) {
1199 				case MOD_MESH:
1200 					R_AddMD3Surfaces( ent );
1201 					break;
1202 				case MOD_MD4:
1203 					R_AddAnimSurfaces( ent );
1204 					break;
1205 #ifdef RAVENMD4
1206 				case MOD_MDR:
1207 					R_MDRAddAnimSurfaces( ent );
1208 					break;
1209 #endif
1210 				case MOD_BRUSH:
1211 					R_AddBrushModelSurfaces( ent );
1212 					break;
1213 				case MOD_BAD:		// null model axis
1214 					if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) {
1215 						break;
1216 					}
1217 					shader = R_GetShaderByHandle( ent->e.customShader );
1218 					R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 );
1219 					break;
1220 				default:
1221 					ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" );
1222 					break;
1223 				}
1224 			}
1225 			break;
1226 		default:
1227 			ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" );
1228 		}
1229 	}
1230 
1231 }
1232 
1233 
1234 /*
1235 ====================
1236 R_GenerateDrawSurfs
1237 ====================
1238 */
R_GenerateDrawSurfs(void)1239 void R_GenerateDrawSurfs( void ) {
1240 	R_AddWorldSurfaces ();
1241 
1242 	R_AddPolygonSurfaces();
1243 
1244 	// set the projection matrix with the minimum zfar
1245 	// now that we have the world bounded
1246 	// this needs to be done before entities are
1247 	// added, because they use the projection
1248 	// matrix for lod calculation
1249 	R_SetupProjection ();
1250 
1251 	R_AddEntitySurfaces ();
1252 }
1253 
1254 /*
1255 ================
1256 R_DebugPolygon
1257 ================
1258 */
R_DebugPolygon(int color,int numPoints,float * points)1259 void R_DebugPolygon( int color, int numPoints, float *points ) {
1260 	int		i;
1261 
1262 	GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
1263 
1264 	// draw solid shade
1265 
1266 	qglColor3f( color&1, (color>>1)&1, (color>>2)&1 );
1267 	qglBegin( GL_POLYGON );
1268 	for ( i = 0 ; i < numPoints ; i++ ) {
1269 		qglVertex3fv( points + i * 3 );
1270 	}
1271 	qglEnd();
1272 
1273 	// draw wireframe outline
1274 	GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
1275 	qglDepthRange( 0, 0 );
1276 	qglColor3f( 1, 1, 1 );
1277 	qglBegin( GL_POLYGON );
1278 	for ( i = 0 ; i < numPoints ; i++ ) {
1279 		qglVertex3fv( points + i * 3 );
1280 	}
1281 	qglEnd();
1282 	qglDepthRange( 0, 1 );
1283 }
1284 
1285 /*
1286 ====================
1287 R_DebugGraphics
1288 
1289 Visualization aid for movement clipping debugging
1290 ====================
1291 */
R_DebugGraphics(void)1292 void R_DebugGraphics( void ) {
1293 	if ( !r_debugSurface->integer ) {
1294 		return;
1295 	}
1296 
1297 	// the render thread can't make callbacks to the main thread
1298 	R_SyncRenderThread();
1299 
1300 	GL_Bind( tr.whiteImage);
1301 	GL_Cull( CT_FRONT_SIDED );
1302 	ri.CM_DrawDebugSurface( R_DebugPolygon );
1303 }
1304 
1305 
1306 /*
1307 ================
1308 R_RenderView
1309 
1310 A view may be either the actual camera view,
1311 or a mirror / remote location
1312 ================
1313 */
R_RenderView(viewParms_t * parms)1314 void R_RenderView (viewParms_t *parms) {
1315 	int		firstDrawSurf;
1316 
1317 	if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) {
1318 		return;
1319 	}
1320 
1321 	tr.viewCount++;
1322 
1323 	tr.viewParms = *parms;
1324 	tr.viewParms.frameSceneNum = tr.frameSceneNum;
1325 	tr.viewParms.frameCount = tr.frameCount;
1326 
1327 	firstDrawSurf = tr.refdef.numDrawSurfs;
1328 
1329 	tr.viewCount++;
1330 
1331 	// set viewParms.world
1332 	R_RotateForViewer ();
1333 
1334 	R_SetupFrustum ();
1335 
1336 	R_GenerateDrawSurfs();
1337 
1338 	R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf );
1339 
1340 	// draw main system development information (surface outlines, etc)
1341 	R_DebugGraphics();
1342 }
1343 
1344 
1345 
1346