1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #if defined(__ppc__) && defined(__APPLE__)
30 #include <vecLib/vecLib.h>
31 #endif
32 #if defined(__GNUC__) && defined(__SSE2__)
33 #include <xmmintrin.h>
34 #endif
35
36 #include "sys/platform.h"
37 #include "framework/Session.h"
38 #include "renderer/RenderWorld_local.h"
39
40 #include "renderer/tr_local.h"
41
42 //====================================================================
43
44 /*
45 ======================
46 idScreenRect::Clear
47 ======================
48 */
Clear()49 void idScreenRect::Clear() {
50 x1 = y1 = 32000;
51 x2 = y2 = -32000;
52 zmin = 0.0f; zmax = 1.0f;
53 }
54
55 /*
56 ======================
57 idScreenRect::AddPoint
58 ======================
59 */
AddPoint(float x,float y)60 void idScreenRect::AddPoint( float x, float y ) {
61 int ix = idMath::FtoiFast( x );
62 int iy = idMath::FtoiFast( y );
63
64 if ( ix < x1 ) {
65 x1 = ix;
66 }
67 if ( ix > x2 ) {
68 x2 = ix;
69 }
70 if ( iy < y1 ) {
71 y1 = iy;
72 }
73 if ( iy > y2 ) {
74 y2 = iy;
75 }
76 }
77
78 /*
79 ======================
80 idScreenRect::Expand
81 ======================
82 */
Expand()83 void idScreenRect::Expand() {
84 x1--;
85 y1--;
86 x2++;
87 y2++;
88 }
89
90 /*
91 ======================
92 idScreenRect::Intersect
93 ======================
94 */
Intersect(const idScreenRect & rect)95 void idScreenRect::Intersect( const idScreenRect &rect ) {
96 if ( rect.x1 > x1 ) {
97 x1 = rect.x1;
98 }
99 if ( rect.x2 < x2 ) {
100 x2 = rect.x2;
101 }
102 if ( rect.y1 > y1 ) {
103 y1 = rect.y1;
104 }
105 if ( rect.y2 < y2 ) {
106 y2 = rect.y2;
107 }
108 }
109
110 /*
111 ======================
112 idScreenRect::Union
113 ======================
114 */
Union(const idScreenRect & rect)115 void idScreenRect::Union( const idScreenRect &rect ) {
116 if ( rect.x1 < x1 ) {
117 x1 = rect.x1;
118 }
119 if ( rect.x2 > x2 ) {
120 x2 = rect.x2;
121 }
122 if ( rect.y1 < y1 ) {
123 y1 = rect.y1;
124 }
125 if ( rect.y2 > y2 ) {
126 y2 = rect.y2;
127 }
128 }
129
130 /*
131 ======================
132 idScreenRect::Equals
133 ======================
134 */
Equals(const idScreenRect & rect) const135 bool idScreenRect::Equals( const idScreenRect &rect ) const {
136 return ( x1 == rect.x1 && x2 == rect.x2 && y1 == rect.y1 && y2 == rect.y2 );
137 }
138
139 /*
140 ======================
141 idScreenRect::IsEmpty
142 ======================
143 */
IsEmpty() const144 bool idScreenRect::IsEmpty() const {
145 return ( x1 > x2 || y1 > y2 );
146 }
147
148 /*
149 ======================
150 R_ScreenRectFromViewFrustumBounds
151 ======================
152 */
R_ScreenRectFromViewFrustumBounds(const idBounds & bounds)153 idScreenRect R_ScreenRectFromViewFrustumBounds( const idBounds &bounds ) {
154 idScreenRect screenRect;
155
156 screenRect.x1 = idMath::FtoiFast( 0.5f * ( 1.0f - bounds[1].y ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ) );
157 screenRect.x2 = idMath::FtoiFast( 0.5f * ( 1.0f - bounds[0].y ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ) );
158 screenRect.y1 = idMath::FtoiFast( 0.5f * ( 1.0f + bounds[0].z ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ) );
159 screenRect.y2 = idMath::FtoiFast( 0.5f * ( 1.0f + bounds[1].z ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ) );
160
161 if ( r_useDepthBoundsTest.GetInteger() ) {
162 R_TransformEyeZToWin( -bounds[0].x, tr.viewDef->projectionMatrix, screenRect.zmin );
163 R_TransformEyeZToWin( -bounds[1].x, tr.viewDef->projectionMatrix, screenRect.zmax );
164 }
165
166 return screenRect;
167 }
168
169 /*
170 ======================
171 R_ShowColoredScreenRect
172 ======================
173 */
R_ShowColoredScreenRect(const idScreenRect & rect,int colorIndex)174 void R_ShowColoredScreenRect( const idScreenRect &rect, int colorIndex ) {
175 if ( !rect.IsEmpty() ) {
176 static idVec4 colors[] = { colorRed, colorGreen, colorBlue, colorYellow, colorMagenta, colorCyan, colorWhite, colorPurple };
177 tr.viewDef->renderWorld->DebugScreenRect( colors[colorIndex & 7], rect, tr.viewDef );
178 }
179 }
180
181 /*
182 ====================
183 R_ToggleSmpFrame
184 ====================
185 */
R_ToggleSmpFrame(void)186 void R_ToggleSmpFrame( void ) {
187 if ( r_lockSurfaces.GetBool() ) {
188 return;
189 }
190 R_FreeDeferredTriSurfs( frameData );
191
192 // clear frame-temporary data
193 frameData_t *frame;
194 frameMemoryBlock_t *block;
195
196 // update the highwater mark
197 R_CountFrameData();
198
199 frame = frameData;
200
201 // reset the memory allocation to the first block
202 frame->alloc = frame->memory;
203
204 // clear all the blocks
205 for ( block = frame->memory ; block ; block = block->next ) {
206 block->used = 0;
207 }
208
209 R_ClearCommandChain();
210 }
211
212
213 //=====================================================
214
215 #define MEMORY_BLOCK_SIZE 0x100000
216
217 /*
218 =====================
219 R_ShutdownFrameData
220 =====================
221 */
R_ShutdownFrameData(void)222 void R_ShutdownFrameData( void ) {
223 frameData_t *frame;
224 frameMemoryBlock_t *block;
225
226 // free any current data
227 frame = frameData;
228 if ( !frame ) {
229 return;
230 }
231
232 R_FreeDeferredTriSurfs( frame );
233
234 frameMemoryBlock_t *nextBlock;
235 for ( block = frame->memory ; block ; block = nextBlock ) {
236 nextBlock = block->next;
237 Mem_Free( block );
238 }
239 Mem_Free( frame );
240 frameData = NULL;
241 }
242
243 /*
244 =====================
245 R_InitFrameData
246 =====================
247 */
R_InitFrameData(void)248 void R_InitFrameData( void ) {
249 int size;
250 frameData_t *frame;
251 frameMemoryBlock_t *block;
252
253 R_ShutdownFrameData();
254
255 frameData = (frameData_t *)Mem_ClearedAlloc( sizeof( *frameData ));
256 frame = frameData;
257 size = MEMORY_BLOCK_SIZE;
258 block = (frameMemoryBlock_t *)Mem_Alloc( size + sizeof( *block ) );
259 if ( !block ) {
260 common->FatalError( "R_InitFrameData: Mem_Alloc() failed" );
261 }
262 block->size = size;
263 block->used = 0;
264 block->next = NULL;
265 frame->memory = block;
266 frame->memoryHighwater = 0;
267
268 R_ToggleSmpFrame();
269 }
270
271 /*
272 ================
273 R_CountFrameData
274 ================
275 */
R_CountFrameData(void)276 int R_CountFrameData( void ) {
277 frameData_t *frame;
278 frameMemoryBlock_t *block;
279 int count;
280
281 count = 0;
282 frame = frameData;
283 for ( block = frame->memory ; block ; block=block->next ) {
284 count += block->used;
285 if ( block == frame->alloc ) {
286 break;
287 }
288 }
289
290 // note if this is a new highwater mark
291 if ( count > frame->memoryHighwater ) {
292 frame->memoryHighwater = count;
293 }
294
295 return count;
296 }
297
298 /*
299 =================
300 R_StaticAlloc
301 =================
302 */
R_StaticAlloc(int bytes)303 void *R_StaticAlloc( int bytes ) {
304 void *buf;
305
306 tr.pc.c_alloc++;
307
308 tr.staticAllocCount += bytes;
309
310 buf = Mem_Alloc( bytes );
311
312 // don't exit on failure on zero length allocations since the old code didn't
313 if ( !buf && ( bytes != 0 ) ) {
314 common->FatalError( "R_StaticAlloc failed on %i bytes", bytes );
315 }
316 return buf;
317 }
318
319 /*
320 =================
321 R_ClearedStaticAlloc
322 =================
323 */
R_ClearedStaticAlloc(int bytes)324 void *R_ClearedStaticAlloc( int bytes ) {
325 void *buf;
326
327 buf = R_StaticAlloc( bytes );
328 SIMDProcessor->Memset( buf, 0, bytes );
329 return buf;
330 }
331
332 /*
333 =================
334 R_StaticFree
335 =================
336 */
R_StaticFree(void * data)337 void R_StaticFree( void *data ) {
338 tr.pc.c_free++;
339 Mem_Free( data );
340 }
341
342 /*
343 ================
344 R_FrameAlloc
345
346 This data will be automatically freed when the
347 current frame's back end completes.
348
349 This should only be called by the front end. The
350 back end shouldn't need to allocate memory.
351
352 If we passed smpFrame in, the back end could
353 alloc memory, because it will always be a
354 different frameData than the front end is using.
355
356 All temporary data, like dynamic tesselations
357 and local spaces are allocated here.
358
359 The memory will not move, but it may not be
360 contiguous with previous allocations even
361 from this frame.
362
363 The memory is NOT zero filled.
364 Should part of this be inlined in a macro?
365 ================
366 */
R_FrameAlloc(int bytes)367 void *R_FrameAlloc( int bytes ) {
368 frameData_t *frame;
369 frameMemoryBlock_t *block;
370 void *buf;
371
372 bytes = (bytes+16)&~15;
373 // see if it can be satisfied in the current block
374 frame = frameData;
375 block = frame->alloc;
376
377 if ( block->size - block->used >= bytes ) {
378 buf = block->base + block->used;
379 block->used += bytes;
380 return buf;
381 }
382
383 // advance to the next memory block if available
384 block = block->next;
385 // create a new block if we are at the end of
386 // the chain
387 if ( !block ) {
388 int size;
389
390 size = MEMORY_BLOCK_SIZE;
391 block = (frameMemoryBlock_t *)Mem_Alloc( size + sizeof( *block ) );
392 if ( !block ) {
393 common->FatalError( "R_FrameAlloc: Mem_Alloc() failed" );
394 }
395 block->size = size;
396 block->used = 0;
397 block->next = NULL;
398 frame->alloc->next = block;
399 }
400
401 // we could fix this if we needed to...
402 if ( bytes > block->size ) {
403 common->FatalError( "R_FrameAlloc of %i exceeded MEMORY_BLOCK_SIZE",
404 bytes );
405 }
406
407 frame->alloc = block;
408
409 block->used = bytes;
410
411 return block->base;
412 }
413
414 /*
415 ==================
416 R_ClearedFrameAlloc
417 ==================
418 */
R_ClearedFrameAlloc(int bytes)419 void *R_ClearedFrameAlloc( int bytes ) {
420 void *r;
421
422 r = R_FrameAlloc( bytes );
423 SIMDProcessor->Memset( r, 0, bytes );
424 return r;
425 }
426
427
428 /*
429 ==================
430 R_FrameFree
431
432 This does nothing at all, as the frame data is reused every frame
433 and can only be stack allocated.
434
435 The only reason for it's existance is so functions that can
436 use either static or frame memory can set function pointers
437 to both alloc and free.
438 ==================
439 */
R_FrameFree(void * data)440 void R_FrameFree( void *data ) {
441 }
442
443
444
445 //==========================================================================
446
R_AxisToModelMatrix(const idMat3 & axis,const idVec3 & origin,float modelMatrix[16])447 void R_AxisToModelMatrix( const idMat3 &axis, const idVec3 &origin, float modelMatrix[16] ) {
448 modelMatrix[0] = axis[0][0];
449 modelMatrix[4] = axis[1][0];
450 modelMatrix[8] = axis[2][0];
451 modelMatrix[12] = origin[0];
452
453 modelMatrix[1] = axis[0][1];
454 modelMatrix[5] = axis[1][1];
455 modelMatrix[9] = axis[2][1];
456 modelMatrix[13] = origin[1];
457
458 modelMatrix[2] = axis[0][2];
459 modelMatrix[6] = axis[1][2];
460 modelMatrix[10] = axis[2][2];
461 modelMatrix[14] = origin[2];
462
463 modelMatrix[3] = 0;
464 modelMatrix[7] = 0;
465 modelMatrix[11] = 0;
466 modelMatrix[15] = 1;
467 }
468
469
470 // FIXME: these assume no skewing or scaling transforms
471
R_LocalPointToGlobal(const float modelMatrix[16],const idVec3 & in,idVec3 & out)472 void R_LocalPointToGlobal( const float modelMatrix[16], const idVec3 &in, idVec3 &out ) {
473 #if defined(__GNUC__) && defined(__SSE2__)
474 __m128 m0, m1, m2, m3;
475 __m128 in0, in1, in2;
476 float i0,i1,i2;
477 i0 = in[0];
478 i1 = in[1];
479 i2 = in[2];
480
481 m0 = _mm_loadu_ps(&modelMatrix[0]);
482 m1 = _mm_loadu_ps(&modelMatrix[4]);
483 m2 = _mm_loadu_ps(&modelMatrix[8]);
484 m3 = _mm_loadu_ps(&modelMatrix[12]);
485
486 in0 = _mm_load1_ps(&i0);
487 in1 = _mm_load1_ps(&i1);
488 in2 = _mm_load1_ps(&i2);
489
490 m0 = _mm_mul_ps(m0, in0);
491 m1 = _mm_mul_ps(m1, in1);
492 m2 = _mm_mul_ps(m2, in2);
493
494 m0 = _mm_add_ps(m0, m1);
495 m0 = _mm_add_ps(m0, m2);
496 m0 = _mm_add_ps(m0, m3);
497
498 _mm_store_ss(&out[0], m0);
499 m1 = (__m128) _mm_shuffle_epi32((__m128i)m0, 0x55);
500 _mm_store_ss(&out[1], m1);
501 m2 = _mm_movehl_ps(m2, m0);
502 _mm_store_ss(&out[2], m2);
503 #else
504 out[0] = in[0] * modelMatrix[0] + in[1] * modelMatrix[4]
505 + in[2] * modelMatrix[8] + modelMatrix[12];
506 out[1] = in[0] * modelMatrix[1] + in[1] * modelMatrix[5]
507 + in[2] * modelMatrix[9] + modelMatrix[13];
508 out[2] = in[0] * modelMatrix[2] + in[1] * modelMatrix[6]
509 + in[2] * modelMatrix[10] + modelMatrix[14];
510 #endif
511 }
512
R_PointTimesMatrix(const float modelMatrix[16],const idVec4 & in,idVec4 & out)513 void R_PointTimesMatrix( const float modelMatrix[16], const idVec4 &in, idVec4 &out ) {
514 out[0] = in[0] * modelMatrix[0] + in[1] * modelMatrix[4]
515 + in[2] * modelMatrix[8] + modelMatrix[12];
516 out[1] = in[0] * modelMatrix[1] + in[1] * modelMatrix[5]
517 + in[2] * modelMatrix[9] + modelMatrix[13];
518 out[2] = in[0] * modelMatrix[2] + in[1] * modelMatrix[6]
519 + in[2] * modelMatrix[10] + modelMatrix[14];
520 out[3] = in[0] * modelMatrix[3] + in[1] * modelMatrix[7]
521 + in[2] * modelMatrix[11] + modelMatrix[15];
522 }
523
R_GlobalPointToLocal(const float modelMatrix[16],const idVec3 & in,idVec3 & out)524 void R_GlobalPointToLocal( const float modelMatrix[16], const idVec3 &in, idVec3 &out ) {
525 idVec3 temp;
526
527 VectorSubtract( in, &modelMatrix[12], temp );
528
529 out[0] = DotProduct( temp, &modelMatrix[0] );
530 out[1] = DotProduct( temp, &modelMatrix[4] );
531 out[2] = DotProduct( temp, &modelMatrix[8] );
532 }
533
R_LocalVectorToGlobal(const float modelMatrix[16],const idVec3 & in,idVec3 & out)534 void R_LocalVectorToGlobal( const float modelMatrix[16], const idVec3 &in, idVec3 &out ) {
535 out[0] = in[0] * modelMatrix[0] + in[1] * modelMatrix[4]
536 + in[2] * modelMatrix[8];
537 out[1] = in[0] * modelMatrix[1] + in[1] * modelMatrix[5]
538 + in[2] * modelMatrix[9];
539 out[2] = in[0] * modelMatrix[2] + in[1] * modelMatrix[6]
540 + in[2] * modelMatrix[10];
541 }
542
R_GlobalVectorToLocal(const float modelMatrix[16],const idVec3 & in,idVec3 & out)543 void R_GlobalVectorToLocal( const float modelMatrix[16], const idVec3 &in, idVec3 &out ) {
544 out[0] = DotProduct( in, &modelMatrix[0] );
545 out[1] = DotProduct( in, &modelMatrix[4] );
546 out[2] = DotProduct( in, &modelMatrix[8] );
547 }
548
R_GlobalPlaneToLocal(const float modelMatrix[16],const idPlane & in,idPlane & out)549 void R_GlobalPlaneToLocal( const float modelMatrix[16], const idPlane &in, idPlane &out ) {
550 out[0] = DotProduct( in, &modelMatrix[0] );
551 out[1] = DotProduct( in, &modelMatrix[4] );
552 out[2] = DotProduct( in, &modelMatrix[8] );
553 out[3] = in[3] + modelMatrix[12] * in[0] + modelMatrix[13] * in[1] + modelMatrix[14] * in[2];
554 }
555
R_LocalPlaneToGlobal(const float modelMatrix[16],const idPlane & in,idPlane & out)556 void R_LocalPlaneToGlobal( const float modelMatrix[16], const idPlane &in, idPlane &out ) {
557 float offset;
558
559 R_LocalVectorToGlobal( modelMatrix, in.Normal(), out.Normal() );
560
561 offset = modelMatrix[12] * out[0] + modelMatrix[13] * out[1] + modelMatrix[14] * out[2];
562 out[3] = in[3] - offset;
563 }
564
565 // transform Z in eye coordinates to window coordinates
R_TransformEyeZToWin(float src_z,const float * projectionMatrix,float & dst_z)566 void R_TransformEyeZToWin( float src_z, const float *projectionMatrix, float &dst_z ) {
567 float clip_z, clip_w;
568
569 // projection
570 clip_z = src_z * projectionMatrix[ 2 + 2 * 4 ] + projectionMatrix[ 2 + 3 * 4 ];
571 clip_w = src_z * projectionMatrix[ 3 + 2 * 4 ] + projectionMatrix[ 3 + 3 * 4 ];
572
573 if ( clip_w <= 0.0f ) {
574 dst_z = 0.0f; // clamp to near plane
575 } else {
576 dst_z = clip_z / clip_w;
577 dst_z = dst_z * 0.5f + 0.5f; // convert to window coords
578 }
579 }
580
581 /*
582 =================
583 R_RadiusCullLocalBox
584
585 A fast, conservative center-to-corner culling test
586 Returns true if the box is outside the given global frustum, (positive sides are out)
587 =================
588 */
R_RadiusCullLocalBox(const idBounds & bounds,const float modelMatrix[16],int numPlanes,const idPlane * planes)589 bool R_RadiusCullLocalBox( const idBounds &bounds, const float modelMatrix[16], int numPlanes, const idPlane *planes ) {
590 int i;
591 float d;
592 idVec3 worldOrigin;
593 float worldRadius;
594 const idPlane *frust;
595
596 if ( r_useCulling.GetInteger() == 0 ) {
597 return false;
598 }
599
600 // transform the surface bounds into world space
601 idVec3 localOrigin = ( bounds[0] + bounds[1] ) * 0.5;
602
603 R_LocalPointToGlobal( modelMatrix, localOrigin, worldOrigin );
604
605 worldRadius = (bounds[0] - localOrigin).Length(); // FIXME: won't be correct for scaled objects
606
607 for ( i = 0 ; i < numPlanes ; i++ ) {
608 frust = planes + i;
609 d = frust->Distance( worldOrigin );
610 if ( d > worldRadius ) {
611 return true; // culled
612 }
613 }
614
615 return false; // no culled
616 }
617
618 /*
619 =================
620 R_CornerCullLocalBox
621
622 Tests all corners against the frustum.
623 Can still generate a few false positives when the box is outside a corner.
624 Returns true if the box is outside the given global frustum, (positive sides are out)
625 =================
626 */
R_CornerCullLocalBox(const idBounds & bounds,const float modelMatrix[16],int numPlanes,const idPlane * planes)627 bool R_CornerCullLocalBox( const idBounds &bounds, const float modelMatrix[16], int numPlanes, const idPlane *planes ) {
628 int i, j;
629 idVec3 transformed[8];
630 float dists[8];
631 idVec3 v;
632 const idPlane *frust;
633
634 // we can disable box culling for experimental timing purposes
635 if ( r_useCulling.GetInteger() < 2 ) {
636 return false;
637 }
638
639 // transform into world space
640 for ( i = 0 ; i < 8 ; i++ ) {
641 v[0] = bounds[i&1][0];
642 v[1] = bounds[(i>>1)&1][1];
643 v[2] = bounds[(i>>2)&1][2];
644
645 R_LocalPointToGlobal( modelMatrix, v, transformed[i] );
646 }
647
648 // check against frustum planes
649 for ( i = 0 ; i < numPlanes ; i++ ) {
650 frust = planes + i;
651 for ( j = 0 ; j < 8 ; j++ ) {
652 dists[j] = frust->Distance( transformed[j] );
653 if ( dists[j] < 0 ) {
654 break;
655 }
656 }
657 if ( j == 8 ) {
658 // all points were behind one of the planes
659 tr.pc.c_box_cull_out++;
660 return true;
661 }
662 }
663
664 tr.pc.c_box_cull_in++;
665
666 return false; // not culled
667 }
668
669 /*
670 =================
671 R_CullLocalBox
672
673 Performs quick test before expensive test
674 Returns true if the box is outside the given global frustum, (positive sides are out)
675 =================
676 */
R_CullLocalBox(const idBounds & bounds,const float modelMatrix[16],int numPlanes,const idPlane * planes)677 bool R_CullLocalBox( const idBounds &bounds, const float modelMatrix[16], int numPlanes, const idPlane *planes ) {
678 if ( R_RadiusCullLocalBox( bounds, modelMatrix, numPlanes, planes ) ) {
679 return true;
680 }
681 return R_CornerCullLocalBox( bounds, modelMatrix, numPlanes, planes );
682 }
683
684 /*
685 ==========================
686 R_TransformModelToClip
687 ==========================
688 */
R_TransformModelToClip(const idVec3 & src,const float * modelMatrix,const float * projectionMatrix,idPlane & eye,idPlane & dst)689 void R_TransformModelToClip( const idVec3 &src, const float *modelMatrix, const float *projectionMatrix, idPlane &eye, idPlane &dst ) {
690 int i;
691
692 for ( i = 0 ; i < 4 ; i++ ) {
693 eye[i] =
694 src[0] * modelMatrix[ i + 0 * 4 ] +
695 src[1] * modelMatrix[ i + 1 * 4 ] +
696 src[2] * modelMatrix[ i + 2 * 4 ] +
697 1 * modelMatrix[ i + 3 * 4 ];
698 }
699
700 for ( i = 0 ; i < 4 ; i++ ) {
701 dst[i] =
702 eye[0] * projectionMatrix[ i + 0 * 4 ] +
703 eye[1] * projectionMatrix[ i + 1 * 4 ] +
704 eye[2] * projectionMatrix[ i + 2 * 4 ] +
705 eye[3] * projectionMatrix[ i + 3 * 4 ];
706 }
707 }
708
709 /*
710 ==========================
711 R_GlobalToNormalizedDeviceCoordinates
712
713 -1 to 1 range in x, y, and z
714 ==========================
715 */
R_GlobalToNormalizedDeviceCoordinates(const idVec3 & global,idVec3 & ndc)716 void R_GlobalToNormalizedDeviceCoordinates( const idVec3 &global, idVec3 &ndc ) {
717 int i;
718 idPlane view;
719 idPlane clip;
720
721 // _D3XP added work on primaryView when no viewDef
722 if ( !tr.viewDef ) {
723
724 for ( i = 0 ; i < 4 ; i ++ ) {
725 view[i] =
726 global[0] * tr.primaryView->worldSpace.modelViewMatrix[ i + 0 * 4 ] +
727 global[1] * tr.primaryView->worldSpace.modelViewMatrix[ i + 1 * 4 ] +
728 global[2] * tr.primaryView->worldSpace.modelViewMatrix[ i + 2 * 4 ] +
729 tr.primaryView->worldSpace.modelViewMatrix[ i + 3 * 4 ];
730 }
731
732 for ( i = 0 ; i < 4 ; i ++ ) {
733 clip[i] =
734 view[0] * tr.primaryView->projectionMatrix[ i + 0 * 4 ] +
735 view[1] * tr.primaryView->projectionMatrix[ i + 1 * 4 ] +
736 view[2] * tr.primaryView->projectionMatrix[ i + 2 * 4 ] +
737 view[3] * tr.primaryView->projectionMatrix[ i + 3 * 4 ];
738 }
739
740 } else {
741
742 for ( i = 0 ; i < 4 ; i ++ ) {
743 view[i] =
744 global[0] * tr.viewDef->worldSpace.modelViewMatrix[ i + 0 * 4 ] +
745 global[1] * tr.viewDef->worldSpace.modelViewMatrix[ i + 1 * 4 ] +
746 global[2] * tr.viewDef->worldSpace.modelViewMatrix[ i + 2 * 4 ] +
747 tr.viewDef->worldSpace.modelViewMatrix[ i + 3 * 4 ];
748 }
749
750
751 for ( i = 0 ; i < 4 ; i ++ ) {
752 clip[i] =
753 view[0] * tr.viewDef->projectionMatrix[ i + 0 * 4 ] +
754 view[1] * tr.viewDef->projectionMatrix[ i + 1 * 4 ] +
755 view[2] * tr.viewDef->projectionMatrix[ i + 2 * 4 ] +
756 view[3] * tr.viewDef->projectionMatrix[ i + 3 * 4 ];
757 }
758
759 }
760
761 ndc[0] = clip[0] / clip[3];
762 ndc[1] = clip[1] / clip[3];
763 ndc[2] = ( clip[2] + clip[3] ) / ( 2 * clip[3] );
764 }
765
766 /*
767 ==========================
768 R_TransformClipToDevice
769
770 Clip to normalized device coordinates
771 ==========================
772 */
R_TransformClipToDevice(const idPlane & clip,const viewDef_t * view,idVec3 & normalized)773 void R_TransformClipToDevice( const idPlane &clip, const viewDef_t *view, idVec3 &normalized ) {
774 normalized[0] = clip[0] / clip[3];
775 normalized[1] = clip[1] / clip[3];
776 normalized[2] = clip[2] / clip[3];
777 }
778
779
780 /*
781 ==========================
782 myGlMultMatrix
783 ==========================
784 */
myGlMultMatrix(const float a[16],const float b[16],float out[16])785 void myGlMultMatrix( const float a[16], const float b[16], float out[16] ) {
786 #if 0
787 int i, j;
788
789 for ( i = 0 ; i < 4 ; i++ ) {
790 for ( j = 0 ; j < 4 ; j++ ) {
791 out[ i * 4 + j ] =
792 a [ i * 4 + 0 ] * b [ 0 * 4 + j ]
793 + a [ i * 4 + 1 ] * b [ 1 * 4 + j ]
794 + a [ i * 4 + 2 ] * b [ 2 * 4 + j ]
795 + a [ i * 4 + 3 ] * b [ 3 * 4 + j ];
796 }
797 }
798 #else
799 out[0*4+0] = a[0*4+0]*b[0*4+0] + a[0*4+1]*b[1*4+0] + a[0*4+2]*b[2*4+0] + a[0*4+3]*b[3*4+0];
800 out[0*4+1] = a[0*4+0]*b[0*4+1] + a[0*4+1]*b[1*4+1] + a[0*4+2]*b[2*4+1] + a[0*4+3]*b[3*4+1];
801 out[0*4+2] = a[0*4+0]*b[0*4+2] + a[0*4+1]*b[1*4+2] + a[0*4+2]*b[2*4+2] + a[0*4+3]*b[3*4+2];
802 out[0*4+3] = a[0*4+0]*b[0*4+3] + a[0*4+1]*b[1*4+3] + a[0*4+2]*b[2*4+3] + a[0*4+3]*b[3*4+3];
803 out[1*4+0] = a[1*4+0]*b[0*4+0] + a[1*4+1]*b[1*4+0] + a[1*4+2]*b[2*4+0] + a[1*4+3]*b[3*4+0];
804 out[1*4+1] = a[1*4+0]*b[0*4+1] + a[1*4+1]*b[1*4+1] + a[1*4+2]*b[2*4+1] + a[1*4+3]*b[3*4+1];
805 out[1*4+2] = a[1*4+0]*b[0*4+2] + a[1*4+1]*b[1*4+2] + a[1*4+2]*b[2*4+2] + a[1*4+3]*b[3*4+2];
806 out[1*4+3] = a[1*4+0]*b[0*4+3] + a[1*4+1]*b[1*4+3] + a[1*4+2]*b[2*4+3] + a[1*4+3]*b[3*4+3];
807 out[2*4+0] = a[2*4+0]*b[0*4+0] + a[2*4+1]*b[1*4+0] + a[2*4+2]*b[2*4+0] + a[2*4+3]*b[3*4+0];
808 out[2*4+1] = a[2*4+0]*b[0*4+1] + a[2*4+1]*b[1*4+1] + a[2*4+2]*b[2*4+1] + a[2*4+3]*b[3*4+1];
809 out[2*4+2] = a[2*4+0]*b[0*4+2] + a[2*4+1]*b[1*4+2] + a[2*4+2]*b[2*4+2] + a[2*4+3]*b[3*4+2];
810 out[2*4+3] = a[2*4+0]*b[0*4+3] + a[2*4+1]*b[1*4+3] + a[2*4+2]*b[2*4+3] + a[2*4+3]*b[3*4+3];
811 out[3*4+0] = a[3*4+0]*b[0*4+0] + a[3*4+1]*b[1*4+0] + a[3*4+2]*b[2*4+0] + a[3*4+3]*b[3*4+0];
812 out[3*4+1] = a[3*4+0]*b[0*4+1] + a[3*4+1]*b[1*4+1] + a[3*4+2]*b[2*4+1] + a[3*4+3]*b[3*4+1];
813 out[3*4+2] = a[3*4+0]*b[0*4+2] + a[3*4+1]*b[1*4+2] + a[3*4+2]*b[2*4+2] + a[3*4+3]*b[3*4+2];
814 out[3*4+3] = a[3*4+0]*b[0*4+3] + a[3*4+1]*b[1*4+3] + a[3*4+2]*b[2*4+3] + a[3*4+3]*b[3*4+3];
815 #endif
816 }
817
818 /*
819 ================
820 R_TransposeGLMatrix
821 ================
822 */
R_TransposeGLMatrix(const float in[16],float out[16])823 void R_TransposeGLMatrix( const float in[16], float out[16] ) {
824 int i, j;
825
826 for ( i = 0 ; i < 4 ; i++ ) {
827 for ( j = 0 ; j < 4 ; j++ ) {
828 out[i*4+j] = in[j*4+i];
829 }
830 }
831 }
832
833 /*
834 =================
835 R_SetViewMatrix
836
837 Sets up the world to view matrix for a given viewParm
838 =================
839 */
R_SetViewMatrix(viewDef_t * viewDef)840 void R_SetViewMatrix( viewDef_t *viewDef ) {
841 idVec3 origin;
842 viewEntity_t *world;
843 float viewerMatrix[16];
844 static float s_flipMatrix[16] = {
845 // convert from our coordinate system (looking down X)
846 // to OpenGL's coordinate system (looking down -Z)
847 0, 0, -1, 0,
848 -1, 0, 0, 0,
849 0, 1, 0, 0,
850 0, 0, 0, 1
851 };
852
853 world = &viewDef->worldSpace;
854
855 memset( world, 0, sizeof(*world) );
856
857 // the model matrix is an identity
858 world->modelMatrix[0*4+0] = 1;
859 world->modelMatrix[1*4+1] = 1;
860 world->modelMatrix[2*4+2] = 1;
861
862 // transform by the camera placement
863 origin = viewDef->renderView.vieworg;
864
865 viewerMatrix[0] = viewDef->renderView.viewaxis[0][0];
866 viewerMatrix[4] = viewDef->renderView.viewaxis[0][1];
867 viewerMatrix[8] = viewDef->renderView.viewaxis[0][2];
868 viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8];
869
870 viewerMatrix[1] = viewDef->renderView.viewaxis[1][0];
871 viewerMatrix[5] = viewDef->renderView.viewaxis[1][1];
872 viewerMatrix[9] = viewDef->renderView.viewaxis[1][2];
873 viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9];
874
875 viewerMatrix[2] = viewDef->renderView.viewaxis[2][0];
876 viewerMatrix[6] = viewDef->renderView.viewaxis[2][1];
877 viewerMatrix[10] = viewDef->renderView.viewaxis[2][2];
878 viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10];
879
880 viewerMatrix[3] = 0;
881 viewerMatrix[7] = 0;
882 viewerMatrix[11] = 0;
883 viewerMatrix[15] = 1;
884
885 // convert from our coordinate system (looking down X)
886 // to OpenGL's coordinate system (looking down -Z)
887 myGlMultMatrix( viewerMatrix, s_flipMatrix, world->modelViewMatrix );
888 }
889
890 /*
891 ===============
892 R_SetupProjection
893
894 This uses the "infinite far z" trick
895 ===============
896 */
R_SetupProjection(void)897 void R_SetupProjection( void ) {
898 float xmin, xmax, ymin, ymax;
899 float width, height;
900 float zNear;
901 float jitterx, jittery;
902 static idRandom random;
903
904 // random jittering is usefull when multiple
905 // frames are going to be blended together
906 // for motion blurred anti-aliasing
907 if ( r_jitter.GetBool() ) {
908 jitterx = random.RandomFloat();
909 jittery = random.RandomFloat();
910 } else {
911 jitterx = jittery = 0;
912 }
913
914 //
915 // set up projection matrix
916 //
917 zNear = r_znear.GetFloat();
918 if ( tr.viewDef->renderView.cramZNear ) {
919 zNear *= 0.25;
920 }
921
922 ymax = zNear * tan( tr.viewDef->renderView.fov_y * idMath::PI / 360.0f );
923 ymin = -ymax;
924
925 xmax = zNear * tan( tr.viewDef->renderView.fov_x * idMath::PI / 360.0f );
926 xmin = -xmax;
927
928 width = xmax - xmin;
929 height = ymax - ymin;
930
931 jitterx = jitterx * width / ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 + 1 );
932 xmin += jitterx;
933 xmax += jitterx;
934 jittery = jittery * height / ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 + 1 );
935 ymin += jittery;
936 ymax += jittery;
937
938 tr.viewDef->projectionMatrix[0] = 2 * zNear / width;
939 tr.viewDef->projectionMatrix[4] = 0;
940 tr.viewDef->projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0
941 tr.viewDef->projectionMatrix[12] = 0;
942
943 tr.viewDef->projectionMatrix[1] = 0;
944 tr.viewDef->projectionMatrix[5] = 2 * zNear / height;
945 tr.viewDef->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0
946 tr.viewDef->projectionMatrix[13] = 0;
947
948 // this is the far-plane-at-infinity formulation, and
949 // crunches the Z range slightly so w=0 vertexes do not
950 // rasterize right at the wraparound point
951 tr.viewDef->projectionMatrix[2] = 0;
952 tr.viewDef->projectionMatrix[6] = 0;
953 tr.viewDef->projectionMatrix[10] = -0.999f;
954 tr.viewDef->projectionMatrix[14] = -2.0f * zNear;
955
956 tr.viewDef->projectionMatrix[3] = 0;
957 tr.viewDef->projectionMatrix[7] = 0;
958 tr.viewDef->projectionMatrix[11] = -1;
959 tr.viewDef->projectionMatrix[15] = 0;
960 }
961
962 /*
963 =================
964 R_SetupViewFrustum
965
966 Setup that culling frustum planes for the current view
967 FIXME: derive from modelview matrix times projection matrix
968 =================
969 */
R_SetupViewFrustum(void)970 static void R_SetupViewFrustum( void ) {
971 int i;
972 float xs, xc;
973 float ang;
974
975 ang = DEG2RAD( tr.viewDef->renderView.fov_x ) * 0.5f;
976 idMath::SinCos( ang, xs, xc );
977
978 tr.viewDef->frustum[0] = xs * tr.viewDef->renderView.viewaxis[0] + xc * tr.viewDef->renderView.viewaxis[1];
979 tr.viewDef->frustum[1] = xs * tr.viewDef->renderView.viewaxis[0] - xc * tr.viewDef->renderView.viewaxis[1];
980
981 ang = DEG2RAD( tr.viewDef->renderView.fov_y ) * 0.5f;
982 idMath::SinCos( ang, xs, xc );
983
984 tr.viewDef->frustum[2] = xs * tr.viewDef->renderView.viewaxis[0] + xc * tr.viewDef->renderView.viewaxis[2];
985 tr.viewDef->frustum[3] = xs * tr.viewDef->renderView.viewaxis[0] - xc * tr.viewDef->renderView.viewaxis[2];
986
987 // plane four is the front clipping plane
988 tr.viewDef->frustum[4] = /* vec3_origin - */ tr.viewDef->renderView.viewaxis[0];
989
990 for ( i = 0; i < 5; i++ ) {
991 // flip direction so positive side faces out (FIXME: globally unify this)
992 tr.viewDef->frustum[i] = -tr.viewDef->frustum[i].Normal();
993 tr.viewDef->frustum[i][3] = -( tr.viewDef->renderView.vieworg * tr.viewDef->frustum[i].Normal() );
994 }
995
996 // eventually, plane five will be the rear clipping plane for fog
997
998 float dNear, dFar, dLeft, dUp;
999
1000 dNear = r_znear.GetFloat();
1001 if ( tr.viewDef->renderView.cramZNear ) {
1002 dNear *= 0.25f;
1003 }
1004
1005 dFar = MAX_WORLD_SIZE;
1006 dLeft = dFar * tan( DEG2RAD( tr.viewDef->renderView.fov_x * 0.5f ) );
1007 dUp = dFar * tan( DEG2RAD( tr.viewDef->renderView.fov_y * 0.5f ) );
1008 tr.viewDef->viewFrustum.SetOrigin( tr.viewDef->renderView.vieworg );
1009 tr.viewDef->viewFrustum.SetAxis( tr.viewDef->renderView.viewaxis );
1010 tr.viewDef->viewFrustum.SetSize( dNear, dFar, dLeft, dUp );
1011 }
1012
1013 /*
1014 ===================
1015 R_ConstrainViewFrustum
1016 ===================
1017 */
R_ConstrainViewFrustum(void)1018 static void R_ConstrainViewFrustum( void ) {
1019 idBounds bounds;
1020
1021 // constrain the view frustum to the total bounds of all visible lights and visible entities
1022 bounds.Clear();
1023 for ( viewLight_t *vLight = tr.viewDef->viewLights; vLight; vLight = vLight->next ) {
1024 bounds.AddBounds( vLight->lightDef->frustumTris->bounds );
1025 }
1026 for ( viewEntity_t *vEntity = tr.viewDef->viewEntitys; vEntity; vEntity = vEntity->next ) {
1027 bounds.AddBounds( vEntity->entityDef->referenceBounds );
1028 }
1029 tr.viewDef->viewFrustum.ConstrainToBounds( bounds );
1030
1031 if ( r_useFrustumFarDistance.GetFloat() > 0.0f ) {
1032 tr.viewDef->viewFrustum.MoveFarDistance( r_useFrustumFarDistance.GetFloat() );
1033 }
1034 }
1035
1036 /*
1037 ==========================================================================================
1038
1039 DRAWSURF SORTING
1040
1041 ==========================================================================================
1042 */
1043
1044
1045 /*
1046 =======================
1047 R_QsortSurfaces
1048
1049 =======================
1050 */
R_QsortSurfaces(const void * a,const void * b)1051 static int R_QsortSurfaces( const void *a, const void *b ) {
1052 const drawSurf_t *ea, *eb;
1053
1054 ea = *(drawSurf_t **)a;
1055 eb = *(drawSurf_t **)b;
1056
1057 if ( ea->sort < eb->sort ) {
1058 return -1;
1059 }
1060 if ( ea->sort > eb->sort ) {
1061 return 1;
1062 }
1063 return 0;
1064 }
1065
1066
1067 /*
1068 =================
1069 R_SortDrawSurfs
1070 =================
1071 */
R_SortDrawSurfs(void)1072 static void R_SortDrawSurfs( void ) {
1073 // sort the drawsurfs by sort type, then orientation, then shader
1074 qsort( tr.viewDef->drawSurfs, tr.viewDef->numDrawSurfs, sizeof( tr.viewDef->drawSurfs[0] ),
1075 R_QsortSurfaces );
1076 }
1077
1078
1079
1080 //========================================================================
1081
1082
1083 //==============================================================================
1084
1085
1086
1087 /*
1088 ================
1089 R_RenderView
1090
1091 A view may be either the actual camera view,
1092 a mirror / remote location, or a 3D view on a gui surface.
1093
1094 Parms will typically be allocated with R_FrameAlloc
1095 ================
1096 */
R_RenderView(viewDef_t * parms)1097 void R_RenderView( viewDef_t *parms ) {
1098 viewDef_t *oldView;
1099
1100 if ( parms->renderView.width <= 0 || parms->renderView.height <= 0 ) {
1101 return;
1102 }
1103
1104 tr.viewCount++;
1105
1106 // save view in case we are a subview
1107 oldView = tr.viewDef;
1108
1109 tr.viewDef = parms;
1110
1111 tr.sortOffset = 0;
1112
1113 // set the matrix for world space to eye space
1114 R_SetViewMatrix( tr.viewDef );
1115
1116 // the four sides of the view frustum are needed
1117 // for culling and portal visibility
1118 R_SetupViewFrustum();
1119
1120 // we need to set the projection matrix before doing
1121 // portal-to-screen scissor box calculations
1122 R_SetupProjection();
1123
1124 // identify all the visible portalAreas, and the entityDefs and
1125 // lightDefs that are in them and pass culling.
1126 static_cast<idRenderWorldLocal *>(parms->renderWorld)->FindViewLightsAndEntities();
1127
1128 // constrain the view frustum to the view lights and entities
1129 R_ConstrainViewFrustum();
1130
1131 // make sure that interactions exist for all light / entity combinations
1132 // that are visible
1133 // add any pre-generated light shadows, and calculate the light shader values
1134 R_AddLightSurfaces();
1135
1136 // adds ambient surfaces and create any necessary interaction surfaces to add to the light
1137 // lists
1138 R_AddModelSurfaces();
1139
1140 // any viewLight that didn't have visible surfaces can have it's shadows removed
1141 R_RemoveUnecessaryViewLights();
1142
1143 // sort all the ambient surfaces for translucency ordering
1144 R_SortDrawSurfs();
1145
1146 // generate any subviews (mirrors, cameras, etc) before adding this view
1147 if ( R_GenerateSubViews() ) {
1148 // if we are debugging subviews, allow the skipping of the
1149 // main view draw
1150 if ( r_subviewOnly.GetBool() ) {
1151 return;
1152 }
1153 }
1154
1155 // write everything needed to the demo file
1156 if ( session->writeDemo ) {
1157 static_cast<idRenderWorldLocal *>(parms->renderWorld)->WriteVisibleDefs( tr.viewDef );
1158 }
1159
1160 // add the rendering commands for this viewDef
1161 R_AddDrawViewCmd( parms );
1162
1163 // restore view in case we are a subview
1164 tr.viewDef = oldView;
1165 }
1166