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