1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP 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 RTCW SP 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP 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 RTCW SP 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 #include "tr_local.h"
30 #include "qgl.h"
31 
32 backEndData_t  *backEndData;
33 backEndState_t backEnd;
34 
35 
36 static float s_flipMatrix[16] = {
37 	// convert from our coordinate system (looking down X)
38 	// to OpenGL's coordinate system (looking down -Z)
39 	0, 0, -1, 0,
40 	-1, 0, 0, 0,
41 	0, 1, 0, 0,
42 	0, 0, 0, 1
43 };
44 
45 
46 /*
47 ** GL_Bind
48 */
GL_Bind(image_t * image)49 void GL_Bind( image_t *image ) {
50 	int texnum;
51 
52 	if ( !image ) {
53 		ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" );
54 		texnum = tr.defaultImage->texnum;
55 	} else {
56 		texnum = image->texnum;
57 	}
58 
59 	if ( r_nobind->integer && tr.dlightImage ) {        // performance evaluation option
60 		texnum = tr.dlightImage->texnum;
61 	}
62 
63 	if ( glState.currenttextures[glState.currenttmu] != texnum ) {
64 		if ( image ) {
65 			image->frameUsed = tr.frameCount;
66 		}
67 		glState.currenttextures[glState.currenttmu] = texnum;
68 		qglBindTexture( GL_TEXTURE_2D, texnum );
69 	}
70 }
71 
72 /*
73 ** GL_SelectTexture
74 */
GL_SelectTexture(int unit)75 void GL_SelectTexture( int unit ) {
76 	if ( glState.currenttmu == unit ) {
77 		return;
78 	}
79 
80 	if ( unit == 0 ) {
81 		qglActiveTextureARB( GL_TEXTURE0_ARB );
82 		GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE0_ARB )\n" );
83 		qglClientActiveTextureARB( GL_TEXTURE0_ARB );
84 		GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" );
85 	} else if ( unit == 1 )   {
86 		qglActiveTextureARB( GL_TEXTURE1_ARB );
87 		GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE1_ARB )\n" );
88 		qglClientActiveTextureARB( GL_TEXTURE1_ARB );
89 		GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" );
90 	} else {
91 		ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit );
92 	}
93 
94 	glState.currenttmu = unit;
95 }
96 
97 
98 /*
99 ** GL_BindMultitexture
100 */
GL_BindMultitexture(image_t * image0,GLuint env0,image_t * image1,GLuint env1)101 void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) {
102 	int texnum0, texnum1;
103 
104 	texnum0 = image0->texnum;
105 	texnum1 = image1->texnum;
106 
107 	if ( r_nobind->integer && tr.dlightImage ) {        // performance evaluation option
108 		texnum0 = texnum1 = tr.dlightImage->texnum;
109 	}
110 
111 	if ( glState.currenttextures[1] != texnum1 ) {
112 		GL_SelectTexture( 1 );
113 		image1->frameUsed = tr.frameCount;
114 		glState.currenttextures[1] = texnum1;
115 		qglBindTexture( GL_TEXTURE_2D, texnum1 );
116 	}
117 	if ( glState.currenttextures[0] != texnum0 ) {
118 		GL_SelectTexture( 0 );
119 		image0->frameUsed = tr.frameCount;
120 		glState.currenttextures[0] = texnum0;
121 		qglBindTexture( GL_TEXTURE_2D, texnum0 );
122 	}
123 }
124 
125 
126 /*
127 ** GL_Cull
128 */
GL_Cull(int cullType)129 void GL_Cull( int cullType ) {
130 	if ( glState.faceCulling == cullType ) {
131 		return;
132 	}
133 
134 	glState.faceCulling = cullType;
135 
136 	if ( cullType == CT_TWO_SIDED )
137 	{
138 		qglDisable( GL_CULL_FACE );
139 	}
140 	else
141 	{
142 		qboolean cullFront;
143 		qglEnable( GL_CULL_FACE );
144 
145 		cullFront = (cullType == CT_FRONT_SIDED);
146 		if ( backEnd.viewParms.isMirror )
147 		{
148 			cullFront = !cullFront;
149 		}
150 
151 		qglCullFace( cullFront ? GL_FRONT : GL_BACK );
152 	}
153 }
154 
155 /*
156 ** GL_TexEnv
157 */
GL_TexEnv(int env)158 void GL_TexEnv( int env ) {
159 	if ( env == glState.texEnv[glState.currenttmu] ) {
160 		return;
161 	}
162 
163 	glState.texEnv[glState.currenttmu] = env;
164 
165 
166 	switch ( env )
167 	{
168 	case GL_MODULATE:
169 		qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
170 		break;
171 	case GL_REPLACE:
172 		qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
173 		break;
174 	case GL_DECAL:
175 		qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
176 		break;
177 	case GL_ADD:
178 		qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
179 		break;
180 	default:
181 		ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env );
182 		break;
183 	}
184 }
185 
186 /*
187 ** GL_State
188 **
189 ** This routine is responsible for setting the most commonly changed state
190 ** in Q3.
191 */
GL_State(unsigned long stateBits)192 void GL_State( unsigned long stateBits ) {
193 	unsigned long diff = stateBits ^ glState.glStateBits;
194 
195 	if ( !diff ) {
196 		return;
197 	}
198 
199 	//
200 	// check depthFunc bits
201 	//
202 	if ( diff & GLS_DEPTHFUNC_EQUAL ) {
203 		if ( stateBits & GLS_DEPTHFUNC_EQUAL ) {
204 			qglDepthFunc( GL_EQUAL );
205 		} else
206 		{
207 			qglDepthFunc( GL_LEQUAL );
208 		}
209 	}
210 
211 	//
212 	// check blend bits
213 	//
214 	if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) {
215 		GLenum srcFactor = GL_ONE, dstFactor = GL_ONE;
216 
217 		if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) {
218 			switch ( stateBits & GLS_SRCBLEND_BITS )
219 			{
220 			case GLS_SRCBLEND_ZERO:
221 				srcFactor = GL_ZERO;
222 				break;
223 			case GLS_SRCBLEND_ONE:
224 				srcFactor = GL_ONE;
225 				break;
226 			case GLS_SRCBLEND_DST_COLOR:
227 				srcFactor = GL_DST_COLOR;
228 				break;
229 			case GLS_SRCBLEND_ONE_MINUS_DST_COLOR:
230 				srcFactor = GL_ONE_MINUS_DST_COLOR;
231 				break;
232 			case GLS_SRCBLEND_SRC_ALPHA:
233 				srcFactor = GL_SRC_ALPHA;
234 				break;
235 			case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA:
236 				srcFactor = GL_ONE_MINUS_SRC_ALPHA;
237 				break;
238 			case GLS_SRCBLEND_DST_ALPHA:
239 				srcFactor = GL_DST_ALPHA;
240 				break;
241 			case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA:
242 				srcFactor = GL_ONE_MINUS_DST_ALPHA;
243 				break;
244 			case GLS_SRCBLEND_ALPHA_SATURATE:
245 				srcFactor = GL_SRC_ALPHA_SATURATE;
246 				break;
247 			default:
248 				ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" );
249 				break;
250 			}
251 
252 			switch ( stateBits & GLS_DSTBLEND_BITS )
253 			{
254 			case GLS_DSTBLEND_ZERO:
255 				dstFactor = GL_ZERO;
256 				break;
257 			case GLS_DSTBLEND_ONE:
258 				dstFactor = GL_ONE;
259 				break;
260 			case GLS_DSTBLEND_SRC_COLOR:
261 				dstFactor = GL_SRC_COLOR;
262 				break;
263 			case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR:
264 				dstFactor = GL_ONE_MINUS_SRC_COLOR;
265 				break;
266 			case GLS_DSTBLEND_SRC_ALPHA:
267 				dstFactor = GL_SRC_ALPHA;
268 				break;
269 			case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA:
270 				dstFactor = GL_ONE_MINUS_SRC_ALPHA;
271 				break;
272 			case GLS_DSTBLEND_DST_ALPHA:
273 				dstFactor = GL_DST_ALPHA;
274 				break;
275 			case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA:
276 				dstFactor = GL_ONE_MINUS_DST_ALPHA;
277 				break;
278 			default:
279 				ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits" );
280 				break;
281 			}
282 
283 			qglEnable( GL_BLEND );
284 			qglBlendFunc( srcFactor, dstFactor );
285 		} else
286 		{
287 			qglDisable( GL_BLEND );
288 		}
289 	}
290 
291 	//
292 	// check depthmask
293 	//
294 	if ( diff & GLS_DEPTHMASK_TRUE ) {
295 		if ( stateBits & GLS_DEPTHMASK_TRUE ) {
296 			qglDepthMask( GL_TRUE );
297 		} else
298 		{
299 			qglDepthMask( GL_FALSE );
300 		}
301 	}
302 
303 	//
304 	// fill/line mode
305 	//
306 	if ( diff & GLS_POLYMODE_LINE ) {
307 #ifndef USE_OPENGLES
308 		if ( stateBits & GLS_POLYMODE_LINE ) {
309 			qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
310 		} else
311 		{
312 			qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
313 		}
314 #endif
315 	}
316 
317 	//
318 	// depthtest
319 	//
320 	if ( diff & GLS_DEPTHTEST_DISABLE ) {
321 		if ( stateBits & GLS_DEPTHTEST_DISABLE ) {
322 			qglDisable( GL_DEPTH_TEST );
323 		} else
324 		{
325 			qglEnable( GL_DEPTH_TEST );
326 		}
327 	}
328 
329 	//
330 	// alpha test
331 	//
332 	if ( diff & GLS_ATEST_BITS ) {
333 		switch ( stateBits & GLS_ATEST_BITS )
334 		{
335 		case 0:
336 			qglDisable( GL_ALPHA_TEST );
337 			break;
338 		case GLS_ATEST_GT_0:
339 			qglEnable( GL_ALPHA_TEST );
340 			qglAlphaFunc( GL_GREATER, 0.0f );
341 			break;
342 		case GLS_ATEST_LT_80:
343 			qglEnable( GL_ALPHA_TEST );
344 			qglAlphaFunc( GL_LESS, 0.5f );
345 			break;
346 		case GLS_ATEST_GE_80:
347 			qglEnable( GL_ALPHA_TEST );
348 			qglAlphaFunc( GL_GEQUAL, 0.5f );
349 			break;
350 		default:
351 			assert( 0 );
352 			break;
353 		}
354 	}
355 
356 	glState.glStateBits = stateBits;
357 }
358 
359 
360 
361 /*
362 ================
363 RB_Hyperspace
364 
365 A player has predicted a teleport, but hasn't arrived yet
366 ================
367 */
RB_Hyperspace(void)368 static void RB_Hyperspace( void ) {
369 	float c;
370 
371 	if ( !backEnd.isHyperspace ) {
372 		// do initialization shit
373 	}
374 
375 	c = ( backEnd.refdef.time & 255 ) / 255.0f;
376 	qglClearColor( c, c, c, 1 );
377 	qglClear( GL_COLOR_BUFFER_BIT );
378 
379 	backEnd.isHyperspace = qtrue;
380 }
381 
382 
SetViewportAndScissor(void)383 static void SetViewportAndScissor( void ) {
384 	qglMatrixMode( GL_PROJECTION );
385 	qglLoadMatrixf( backEnd.viewParms.projectionMatrix );
386 	qglMatrixMode( GL_MODELVIEW );
387 
388 	// set the window clipping
389 	qglViewport(    backEnd.viewParms.viewportX,
390 					backEnd.viewParms.viewportY,
391 					backEnd.viewParms.viewportWidth,
392 					backEnd.viewParms.viewportHeight );
393 
394 // TODO: insert handling for widescreen?  (when looking through camera)
395 	qglScissor(     backEnd.viewParms.viewportX,
396 					backEnd.viewParms.viewportY,
397 					backEnd.viewParms.viewportWidth,
398 					backEnd.viewParms.viewportHeight );
399 }
400 
401 /*
402 =================
403 RB_BeginDrawingView
404 
405 Any mirrored or portaled views have already been drawn, so prepare
406 to actually render the visible surfaces for this view
407 =================
408 */
RB_BeginDrawingView(void)409 void RB_BeginDrawingView( void ) {
410 	int clearBits = 0;
411 
412 	// sync with gl if needed
413 	if ( r_finish->integer == 1 && !glState.finishCalled ) {
414 		qglFinish();
415 		glState.finishCalled = qtrue;
416 	}
417 	if ( r_finish->integer == 0 ) {
418 		glState.finishCalled = qtrue;
419 	}
420 
421 	// we will need to change the projection matrix before drawing
422 	// 2D images again
423 	backEnd.projection2D = qfalse;
424 
425 	//
426 	// set the modelview matrix for the viewer
427 	//
428 	SetViewportAndScissor();
429 
430 	// ensures that depth writes are enabled for the depth clear
431 	GL_State( GLS_DEFAULT );
432 
433 
434 ////////// (SA) modified to ensure one glclear() per frame at most
435 
436 	// clear relevant buffers
437 	clearBits = 0;
438 
439 	if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) {
440 		clearBits |= GL_STENCIL_BUFFER_BIT;
441 	}
442 
443 	if ( r_uiFullScreen->integer ) {
444 		clearBits = GL_DEPTH_BUFFER_BIT;    // (SA) always just clear depth for menus
445 
446 	} else if ( skyboxportal ) {
447 		if ( backEnd.refdef.rdflags & RDF_SKYBOXPORTAL ) {   // portal scene, clear whatever is necessary
448 			clearBits |= GL_DEPTH_BUFFER_BIT;
449 
450 			if ( r_fastsky->integer || backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) {  // fastsky: clear color
451 
452 				// try clearing first with the portal sky fog color, then the world fog color, then finally a default
453 				clearBits |= GL_COLOR_BUFFER_BIT;
454 				if ( glfogsettings[FOG_PORTALVIEW].registered ) {
455 					qglClearColor( glfogsettings[FOG_PORTALVIEW].color[0], glfogsettings[FOG_PORTALVIEW].color[1], glfogsettings[FOG_PORTALVIEW].color[2], glfogsettings[FOG_PORTALVIEW].color[3] );
456 				} else if ( glfogNum > FOG_NONE && glfogsettings[FOG_CURRENT].registered )      {
457 					qglClearColor( glfogsettings[FOG_CURRENT].color[0], glfogsettings[FOG_CURRENT].color[1], glfogsettings[FOG_CURRENT].color[2], glfogsettings[FOG_CURRENT].color[3] );
458 				} else {
459 //					qglClearColor ( 1.0, 0.0, 0.0, 1.0 );	// red clear for testing portal sky clear
460 					qglClearColor( 0.5, 0.5, 0.5, 1.0 );
461 				}
462 			} else {                                                    // rendered sky (either clear color or draw quake sky)
463 				if ( glfogsettings[FOG_PORTALVIEW].registered ) {
464 					qglClearColor( glfogsettings[FOG_PORTALVIEW].color[0], glfogsettings[FOG_PORTALVIEW].color[1], glfogsettings[FOG_PORTALVIEW].color[2], glfogsettings[FOG_PORTALVIEW].color[3] );
465 
466 					if ( glfogsettings[FOG_PORTALVIEW].clearscreen ) {    // portal fog requests a screen clear (distance fog rather than quake sky)
467 						clearBits |= GL_COLOR_BUFFER_BIT;
468 					}
469 				}
470 
471 			}
472 		} else {                                        // world scene with portal sky, don't clear any buffers, just set the fog color if there is one
473 
474 			clearBits |= GL_DEPTH_BUFFER_BIT;   // this will go when I get the portal sky rendering way out in the zbuffer (or not writing to zbuffer at all)
475 
476 			if ( glfogNum > FOG_NONE && glfogsettings[FOG_CURRENT].registered ) {
477 				if ( backEnd.refdef.rdflags & RDF_UNDERWATER ) {
478 					if ( glfogsettings[FOG_CURRENT].mode == GL_LINEAR ) {
479 						clearBits |= GL_COLOR_BUFFER_BIT;
480 					}
481 
482 				} else if ( !( r_portalsky->integer ) ) {    // portal skies have been manually turned off, clear bg color
483 					clearBits |= GL_COLOR_BUFFER_BIT;
484 				}
485 
486 				qglClearColor( glfogsettings[FOG_CURRENT].color[0], glfogsettings[FOG_CURRENT].color[1], glfogsettings[FOG_CURRENT].color[2], glfogsettings[FOG_CURRENT].color[3] );
487 			}
488 		}
489 	} else {                                              // world scene with no portal sky
490 		clearBits |= GL_DEPTH_BUFFER_BIT;
491 
492 		// NERVE - SMF - we don't want to clear the buffer when no world model is specified
493 		if ( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) {
494 			clearBits &= ~GL_COLOR_BUFFER_BIT;
495 		}
496 		// -NERVE - SMF
497 		// (SA) well, this is silly then
498 		else if ( r_fastsky->integer ) {   //  || backEnd.refdef.rdflags & RDF_NOWORLDMODEL
499 
500 			clearBits |= GL_COLOR_BUFFER_BIT;
501 
502 			if ( glfogsettings[FOG_CURRENT].registered ) { // try to clear fastsky with current fog color
503 				qglClearColor( glfogsettings[FOG_CURRENT].color[0], glfogsettings[FOG_CURRENT].color[1], glfogsettings[FOG_CURRENT].color[2], glfogsettings[FOG_CURRENT].color[3] );
504 			} else {
505 //				qglClearColor ( 0.0, 0.0, 1.0, 1.0 );	// blue clear for testing world sky clear
506 				qglClearColor( 0.5, 0.5, 0.5, 1.0 );
507 			}
508 		} else {        // world scene, no portal sky, not fastsky, clear color if fog says to, otherwise, just set the clearcolor
509 			if ( glfogsettings[FOG_CURRENT].registered ) { // try to clear fastsky with current fog color
510 				qglClearColor( glfogsettings[FOG_CURRENT].color[0], glfogsettings[FOG_CURRENT].color[1], glfogsettings[FOG_CURRENT].color[2], glfogsettings[FOG_CURRENT].color[3] );
511 
512 				if ( glfogsettings[FOG_CURRENT].clearscreen ) {   // world fog requests a screen clear (distance fog rather than quake sky)
513 					clearBits |= GL_COLOR_BUFFER_BIT;
514 				}
515 			}
516 		}
517 	}
518 
519 
520 	if ( clearBits ) {
521 		qglClear( clearBits );
522 	}
523 
524 //----(SA)	done
525 
526 	if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) {
527 		RB_Hyperspace();
528 		return;
529 	} else
530 	{
531 		backEnd.isHyperspace = qfalse;
532 	}
533 
534 	glState.faceCulling = -1;       // force face culling to set next time
535 
536 	// we will only draw a sun if there was sky rendered in this view
537 	backEnd.skyRenderedThisView = qfalse;
538 
539 	// clip to the plane of the portal
540 	if ( backEnd.viewParms.isPortal ) {
541 		float plane[4];
542 		GLdouble plane2[4];
543 
544 		plane[0] = backEnd.viewParms.portalPlane.normal[0];
545 		plane[1] = backEnd.viewParms.portalPlane.normal[1];
546 		plane[2] = backEnd.viewParms.portalPlane.normal[2];
547 		plane[3] = backEnd.viewParms.portalPlane.dist;
548 
549 		plane2[0] = DotProduct( backEnd.viewParms.or.axis[0], plane );
550 		plane2[1] = DotProduct( backEnd.viewParms.or.axis[1], plane );
551 		plane2[2] = DotProduct( backEnd.viewParms.or.axis[2], plane );
552 		plane2[3] = DotProduct( plane, backEnd.viewParms.or.origin ) - plane[3];
553 
554 		qglLoadMatrixf( s_flipMatrix );
555 		qglClipPlane( GL_CLIP_PLANE0, plane2 );
556 		qglEnable( GL_CLIP_PLANE0 );
557 	} else {
558 		qglDisable( GL_CLIP_PLANE0 );
559 	}
560 }
561 
562 /*
563 ============
564 RB_ZombieFX
565 
566   This is post-tesselation filtering, made especially for the Zombie.
567 ============
568 */
569 
570 extern void GlobalVectorToLocal( const vec3_t in, vec3_t out );
571 extern vec_t VectorLengthSquared( const vec3_t v );
572 
573 #define ZOMBIEFX_MAX_VERTS              2048
574 #define ZOMBIEFX_FADEOUT_TIME_SEC       ( 0.001 * ZOMBIEFX_FADEOUT_TIME )
575 #define ZOMBIEFX_MAX_HITS               128
576 #define ZOMBIEFX_MAX_NEWHITS            4
577 #define ZOMBIEFX_HIT_OKRANGE_SQR        9   // all verts within this range will be hit
578 #define ZOMBIEFX_HIT_MAXRANGE_SQR       36  // each bullet that strikes the bounding box, will effect verts inside this range (allowing for projections onto the mesh)
579 #define ZOMBIEFX_PERHIT_TAKEALPHA       150
580 #define ZOMBIEFX_MAX_HITS_PER_VERT      2
581 
582 static char *zombieFxFleshHitSurfaceNames[2] = {"u_body","l_legs"};
583 
584 // this stores each of the flesh hits for each of the zombies in the game
585 typedef struct {
586 	qboolean isHit;
587 	unsigned short numHits;
588 	unsigned short vertHits[ZOMBIEFX_MAX_HITS]; // bit flags to represent those verts that have been hit
589 	int numNewHits;
590 	vec3_t newHitPos[ZOMBIEFX_MAX_NEWHITS];
591 	vec3_t newHitDir[ZOMBIEFX_MAX_NEWHITS];
592 } trZombieFleshHitverts_t;
593 //
594 trZombieFleshHitverts_t zombieFleshHitVerts[MAX_SP_CLIENTS][2]; // one for upper, one for lower
595 
RB_ZombieFXInit(void)596 void RB_ZombieFXInit( void ) {
597 	memset( zombieFleshHitVerts, 0, sizeof( zombieFleshHitVerts ) );
598 }
599 
RB_ZombieFXAddNewHit(int entityNum,const vec3_t hitPos,const vec3_t hitDir)600 void RB_ZombieFXAddNewHit( int entityNum, const vec3_t hitPos, const vec3_t hitDir ) {
601 	int part = 0;
602 
603 	if ( entityNum == -1 ) {
604 		// hack, reset data
605 		RB_ZombieFXInit();
606 		return;
607 	}
608 
609 	if ( entityNum & ( 1 << 30 ) ) {
610 		part = 1;
611 		entityNum &= ~( 1 << 30 );
612 	}
613 
614 	if ( entityNum >= MAX_SP_CLIENTS ) {
615 		Com_Printf( "RB_ZombieFXAddNewHit: entityNum (%i) outside allowable range (%i)\n", entityNum, MAX_SP_CLIENTS );
616 		return;
617 	}
618 	if ( zombieFleshHitVerts[entityNum][part].numHits + zombieFleshHitVerts[entityNum][part].numNewHits >= ZOMBIEFX_MAX_HITS ) {
619 		// already full of hits
620 		return;
621 	}
622 	if ( zombieFleshHitVerts[entityNum][part].numNewHits >= ZOMBIEFX_MAX_NEWHITS ) {
623 		// just ignore this hit
624 		return;
625 	}
626 	// add it to the list
627 	VectorCopy( hitPos, zombieFleshHitVerts[entityNum][part].newHitPos[zombieFleshHitVerts[entityNum][part].numNewHits] );
628 	VectorCopy( hitDir, zombieFleshHitVerts[entityNum][part].newHitDir[zombieFleshHitVerts[entityNum][part].numNewHits] );
629 	zombieFleshHitVerts[entityNum][part].numNewHits++;
630 }
631 
RB_ZombieFXProcessNewHits(trZombieFleshHitverts_t * fleshHitVerts,int oldNumVerts,int numSurfVerts)632 void RB_ZombieFXProcessNewHits( trZombieFleshHitverts_t *fleshHitVerts, int oldNumVerts, int numSurfVerts ) {
633 	float *xyzTrav, *normTrav;
634 	vec3_t hitPos, hitDir, v, testDir;
635 	float bestHitDist, thisDist;
636 	qboolean foundHit;
637 	int i, j, bestHit = 0;
638 	unsigned short *hitTrav;
639 	byte hitCounts[ZOMBIEFX_MAX_VERTS];     // so we can quickly tell if a particular vert has been hit enough times already
640 
641 	// first build the hitCount list
642 	memset( hitCounts, 0, sizeof( hitCounts ) );
643 	for ( i = 0, hitTrav = fleshHitVerts->vertHits; i < fleshHitVerts->numHits; i++, hitTrav++ ) {
644 		hitCounts[*hitTrav]++;
645 	}
646 
647 	// for each new hit
648 	for ( i = 0; i < fleshHitVerts->numNewHits; i++ ) {
649 		// calc the local hitPos
650 		VectorCopy( fleshHitVerts->newHitPos[i], v );
651 		VectorSubtract( v, backEnd.currentEntity->e.origin, v );
652 		GlobalVectorToLocal( v, hitPos );
653 		// calc the local hitDir
654 		VectorCopy( fleshHitVerts->newHitDir[i], v );
655 		GlobalVectorToLocal( v, hitDir );
656 
657 		// look for close matches
658 		foundHit = qfalse;
659 
660 		// for each vertex
661 		for (   j = 0, bestHitDist = -1, xyzTrav = tess.xyz[oldNumVerts], normTrav = tess.normal[oldNumVerts];
662 				j < numSurfVerts;
663 				j++, xyzTrav += 4, normTrav += 4 ) {
664 
665 			// if this vert has been hit enough times already
666 			if ( hitCounts[j] > ZOMBIEFX_MAX_HITS_PER_VERT ) {
667 				continue;
668 			}
669 			// if this normal faces the wrong way, reject it
670 			if ( DotProduct( normTrav, hitDir ) > 0 ) {
671 				continue;
672 			}
673 			// get the diff vector
674 			VectorSubtract( xyzTrav, hitPos, testDir );
675 			// check for distance within range
676 			thisDist = VectorLengthSquared( testDir );
677 			if ( thisDist < ZOMBIEFX_HIT_OKRANGE_SQR ) {
678 				goto hitCheckDone;
679 			}
680 			thisDist = sqrt( thisDist );
681 			// check for the projection being inside range
682 			VectorMA( hitPos, thisDist, hitDir, v );
683 			VectorSubtract( xyzTrav, v, testDir );
684 			thisDist = VectorLengthSquared( testDir );
685 			if ( thisDist < ZOMBIEFX_HIT_OKRANGE_SQR ) {
686 				goto hitCheckDone;
687 			}
688 			// if we are still struggling to find a hit, then pick the closest outside the OK range
689 			if ( !foundHit ) {
690 				if ( thisDist < ZOMBIEFX_HIT_MAXRANGE_SQR && ( bestHitDist < 0 || thisDist < bestHitDist ) ) {
691 					bestHitDist = thisDist;
692 					bestHit = j;
693 				}
694 			}
695 
696 			// if it gets to here, then it failed
697 			continue;
698 
699 hitCheckDone:
700 
701 			// this vertex was hit
702 			foundHit = qtrue;
703 			// set the appropriate bit-flag
704 			fleshHitVerts->isHit = qtrue;
705 			fleshHitVerts->vertHits[fleshHitVerts->numHits++] = (unsigned short)j;
706 			//if (fleshHitVerts->numHits == ZOMBIEFX_MAX_HITS)
707 			//	break;	// only find one close match per shot
708 			if ( fleshHitVerts->numHits == ZOMBIEFX_MAX_HITS ) {
709 				break;
710 			}
711 		}
712 
713 		if ( fleshHitVerts->numHits == ZOMBIEFX_MAX_HITS ) {
714 			break;
715 		}
716 
717 		// if we didn't find a hit vertex, grab the closest acceptible match
718 		if ( !foundHit && bestHitDist >= 0 ) {
719 			// set the appropriate bit-flag
720 			fleshHitVerts->isHit = qtrue;
721 			fleshHitVerts->vertHits[fleshHitVerts->numHits++] = (unsigned short)bestHit;
722 			if ( fleshHitVerts->numHits == ZOMBIEFX_MAX_HITS ) {
723 				break;
724 			}
725 		}
726 	}
727 
728 	// we've processed any new hits
729 	fleshHitVerts->numNewHits = 0;
730 }
731 
RB_ZombieFXShowFleshHits(trZombieFleshHitverts_t * fleshHitVerts,int oldNumVerts,int numSurfVerts)732 void RB_ZombieFXShowFleshHits( trZombieFleshHitverts_t *fleshHitVerts, int oldNumVerts, int numSurfVerts ) {
733 	byte *vertColors;
734 	unsigned short *vertHits;
735 	int i;
736 
737 	vertColors = tess.vertexColors[oldNumVerts];
738 	vertHits = fleshHitVerts->vertHits;
739 
740 	// for each hit entry, adjust that verts alpha component
741 	for ( i = 0; i < fleshHitVerts->numHits; i++, vertHits++ ) {
742 		if ( vertColors[( *vertHits ) * 4 + 3] < ZOMBIEFX_PERHIT_TAKEALPHA ) {
743 			vertColors[( *vertHits ) * 4 + 3] = 0;
744 		} else {
745 			vertColors[( *vertHits ) * 4 + 3] -= ZOMBIEFX_PERHIT_TAKEALPHA;
746 		}
747 	}
748 }
749 
RB_ZombieFXDecompose(int oldNumVerts,int numSurfVerts,float deltaTimeScale)750 void RB_ZombieFXDecompose( int oldNumVerts, int numSurfVerts, float deltaTimeScale ) {
751 	byte *vertColors;
752 	float   *xyz, *norm;
753 	int i;
754 	float alpha;
755 
756 	vertColors = tess.vertexColors[oldNumVerts];
757 	xyz = tess.xyz[oldNumVerts];
758 	norm = tess.normal[oldNumVerts];
759 
760 	for ( i = 0; i < numSurfVerts; i++, vertColors += 4, xyz += 4, norm += 4 ) {
761 		alpha = 255.0 * ( (float)( 1 + i % 3 ) / 3.0 ) * deltaTimeScale * 2;
762 		if ( alpha > 255.0 ) {
763 			alpha = 255.0;
764 		}
765 		if ( (float)vertColors[3] - alpha < 0 ) {
766 			vertColors[3] = 0;
767 		} else {
768 			vertColors[3] -= (byte)alpha;
769 		}
770 
771 		// skin shrinks with age
772 		VectorMA( xyz, -2.0 * deltaTimeScale, norm, xyz );
773 	}
774 }
775 
RB_ZombieFXFullAlpha(int oldNumVerts,int numSurfVerts)776 void RB_ZombieFXFullAlpha( int oldNumVerts, int numSurfVerts ) {
777 	byte *vertColors;
778 	int i;
779 
780 	vertColors = tess.vertexColors[oldNumVerts];
781 
782 	for ( i = 0; i < numSurfVerts; i++, vertColors += 4 ) {
783 		vertColors[3] = 255;
784 	}
785 }
786 
RB_ZombieFX(int part,drawSurf_t * drawSurf,int oldNumVerts,int oldNumIndex)787 void RB_ZombieFX( int part, drawSurf_t *drawSurf, int oldNumVerts, int oldNumIndex ) {
788 	int numSurfVerts;
789 	float deltaTime;
790 	char    *surfName;
791 	trZombieFleshHitverts_t *fleshHitVerts;
792 
793 	// Central point for Zombie post-tess processing. Various effects can be added from this point
794 
795 	if ( *drawSurf->surface == SF_MD3 ) {
796 		surfName = ( (md3Surface_t *)drawSurf->surface )->name;
797 	} else if ( *drawSurf->surface == SF_MDC ) {
798 		surfName = ( (mdcSurface_t *)drawSurf->surface )->name;
799 	} else {
800 		Com_Printf( "RB_ZombieFX: unknown surface type\n" );
801 		return;
802 	}
803 
804 	// ignore all surfaces starting with u_sk (skeleton)
805 	if ( !Q_strncmp( surfName, "u_sk", 4 ) ) {
806 		return;
807 	}
808 	// legs
809 	if ( !Q_strncmp( surfName, "l_sk", 4 ) ) {
810 		return;
811 	}
812 	// head
813 	if ( !Q_strncmp( surfName, "h_sk", 4 ) ) {
814 		return;
815 	}
816 
817 	numSurfVerts = tess.numVertexes - oldNumVerts;
818 
819 	if ( numSurfVerts > ZOMBIEFX_MAX_VERTS ) {
820 		Com_Printf( "RB_ZombieFX: exceeded ZOMBIEFX_MAX_VERTS\n" );
821 		return;
822 	}
823 
824 	deltaTime = backEnd.currentEntity->e.shaderTime;
825 	if ( ZOMBIEFX_FADEOUT_TIME_SEC < deltaTime ) {
826 		// nothing to do, it's done fading out
827 		tess.numVertexes = oldNumVerts;
828 		tess.numIndexes = oldNumIndex;
829 		return;
830 	}
831 
832 	fleshHitVerts = &zombieFleshHitVerts[backEnd.currentEntity->e.entityNum][part];
833 
834 	// set everything to full alpha
835 	RB_ZombieFXFullAlpha( oldNumVerts, numSurfVerts );
836 
837 	// if this is the chest surface, do flesh hits
838 	if ( !Q_stricmp( surfName, zombieFxFleshHitSurfaceNames[part] ) ) {
839 
840 		// check for any new bullet impacts that need to be scanned for triangle collisions
841 		if ( fleshHitVerts->numNewHits ) {
842 			RB_ZombieFXProcessNewHits( fleshHitVerts, oldNumVerts, numSurfVerts );
843 		}
844 
845 		// hide vertices marked as being torn off
846 		if ( fleshHitVerts->isHit ) {
847 			RB_ZombieFXShowFleshHits( fleshHitVerts, oldNumVerts, numSurfVerts );
848 		}
849 	}
850 
851 	// decompose?
852 	if ( deltaTime ) {
853 		RB_ZombieFXDecompose( oldNumVerts, numSurfVerts, deltaTime / ZOMBIEFX_FADEOUT_TIME_SEC );
854 	}
855 
856 }
857 
858 
859 /*
860 ==================
861 RB_RenderDrawSurfList
862 ==================
863 */
RB_RenderDrawSurfList(drawSurf_t * drawSurfs,int numDrawSurfs)864 void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) {
865 	shader_t        *shader, *oldShader;
866 	int fogNum, oldFogNum;
867 	int entityNum, oldEntityNum;
868 	int dlighted, oldDlighted;
869 	qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair;
870 	int i;
871 	drawSurf_t      *drawSurf;
872 	int oldSort;
873 	double originalTime;
874 	int oldNumVerts, oldNumIndex;
875 //GR - tessellation flag
876 	int atiTess = 0, oldAtiTess;
877 
878 	// save original time for entity shader offsets
879 	originalTime = backEnd.refdef.floatTime;
880 
881 	// clear the z buffer, set the modelview, etc
882 	RB_BeginDrawingView();
883 
884 	// draw everything
885 	oldEntityNum = -1;
886 	backEnd.currentEntity = &tr.worldEntity;
887 	oldShader = NULL;
888 	oldFogNum = -1;
889 	oldDepthRange = qfalse;
890 	wasCrosshair = qfalse;
891 	oldDlighted = qfalse;
892 	oldSort = -1;
893 	depthRange = qfalse;
894 // GR - tessellation also forces to draw everything
895 	oldAtiTess = -1;
896 
897 	backEnd.pc.c_surfaces += numDrawSurfs;
898 
899 	for ( i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++ ) {
900 		if ( drawSurf->sort == oldSort ) {
901 			// fast path, same as previous sort
902 			oldNumVerts = tess.numVertexes;
903 			oldNumIndex = tess.numIndexes;
904 
905 			rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
906 
907 			// RF, convert the newly created vertexes into dust particles, and overwrite
908 			if (backEnd.currentEntity->e.reFlags & REFLAG_ZOMBIEFX) {
909 				RB_ZombieFX( 0, drawSurf, oldNumVerts, oldNumIndex );
910 			}
911 			else if (backEnd.currentEntity->e.reFlags & REFLAG_ZOMBIEFX2) {
912 				RB_ZombieFX( 1, drawSurf, oldNumVerts, oldNumIndex );
913 			}
914 			continue;
915 		}
916 		oldSort = drawSurf->sort;
917 // GR - also extract tesselation flag
918 		R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &atiTess );
919 
920 		//
921 		// change the tess parameters if needed
922 		// a "entityMergable" shader is a shader that can have surfaces from seperate
923 		// entities merged into a single batch, like smoke and blood puff sprites
924 		if ( shader != NULL && ( shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted
925 // GR - force draw on tessellation flag change
926 			 || ( atiTess != oldAtiTess )
927 			 || ( entityNum != oldEntityNum && !shader->entityMergable ) ) ){
928 			if ( oldShader != NULL ) {
929 // GR - pass tessellation flag to the shader command
930 //		make sure to use oldAtiTess!!!
931 				tess.ATI_tess = ( oldAtiTess == ATI_TESS_TRUFORM );
932 
933 				RB_EndSurface();
934 			}
935 			RB_BeginSurface( shader, fogNum );
936 			oldShader = shader;
937 			oldFogNum = fogNum;
938 			oldDlighted = dlighted;
939 // GR - update old tessellation flag
940 			oldAtiTess = atiTess;
941 		}
942 
943 		//
944 		// change the modelview matrix if needed
945 		//
946 		if ( entityNum != oldEntityNum ) {
947 			depthRange = isCrosshair = qfalse;
948 
949 			if ( entityNum != REFENTITYNUM_WORLD ) {
950 				backEnd.currentEntity = &backEnd.refdef.entities[entityNum];
951 
952 				// FIXME: e.shaderTime must be passed as int to avoid fp-precision loss issues
953 				backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime;
954 
955 				// we have to reset the shaderTime as well otherwise image animations start
956 				// from the wrong frame
957 //				tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
958 
959 				// set up the transformation matrix
960 				R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or );
961 
962 				// set up the dynamic lighting if needed
963 				if ( backEnd.currentEntity->needDlights ) {
964 					R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or );
965 				}
966 
967 				if ( backEnd.currentEntity->e.renderfx & RF_DEPTHHACK ) {
968 					// hack the depth range to prevent view model from poking into walls
969 					depthRange = qtrue;
970 
971 					if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR)
972 						isCrosshair = qtrue;
973 				}
974 			} else {
975 				backEnd.currentEntity = &tr.worldEntity;
976 				backEnd.refdef.floatTime = originalTime;
977 				backEnd.or = backEnd.viewParms.world;
978 
979 				// we have to reset the shaderTime as well otherwise image animations on
980 				// the world (like water) continue with the wrong frame
981 //				tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
982 
983 				R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or );
984 			}
985 
986 			qglLoadMatrixf( backEnd.or.modelMatrix );
987 
988 			//
989 			// change depthrange. Also change projection matrix so first person weapon does not look like coming
990 			// out of the screen.
991 			//
992 			if (oldDepthRange != depthRange || wasCrosshair != isCrosshair)
993 			{
994 				if (depthRange)
995 				{
996 					if(backEnd.viewParms.stereoFrame != STEREO_CENTER)
997 					{
998 						if(isCrosshair)
999 						{
1000 							if(oldDepthRange)
1001 							{
1002 								// was not a crosshair but now is, change back proj matrix
1003 								qglMatrixMode(GL_PROJECTION);
1004 								qglLoadMatrixf(backEnd.viewParms.projectionMatrix);
1005 								qglMatrixMode(GL_MODELVIEW);
1006 							}
1007 						}
1008 						else
1009 						{
1010 							viewParms_t temp = backEnd.viewParms;
1011 
1012 							R_SetupProjection(&temp, r_znear->value, qfalse);
1013 
1014 							qglMatrixMode(GL_PROJECTION);
1015 							qglLoadMatrixf(temp.projectionMatrix);
1016 							qglMatrixMode(GL_MODELVIEW);
1017 						}
1018 					}
1019 
1020 					if(!oldDepthRange)
1021 						qglDepthRange (0, 0.3);
1022 				}
1023 				else
1024 				{
1025 					if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER)
1026 					{
1027 						qglMatrixMode(GL_PROJECTION);
1028 						qglLoadMatrixf(backEnd.viewParms.projectionMatrix);
1029 						qglMatrixMode(GL_MODELVIEW);
1030 					}
1031 					qglDepthRange( 0, 1 );
1032 				}
1033 
1034 				oldDepthRange = depthRange;
1035 				wasCrosshair = isCrosshair;
1036 			}
1037 
1038 			oldEntityNum = entityNum;
1039 		}
1040 
1041 		// RF, ZOMBIEFX, store the tess indexes, so we can grab the calculated
1042 		// vertex positions and normals, and convert them into dust particles
1043 		oldNumVerts = tess.numVertexes;
1044 		oldNumIndex = tess.numIndexes;
1045 
1046 		// add the triangles for this surface
1047 		rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface );
1048 
1049 		// RF, convert the newly created vertexes into dust particles, and overwrite
1050 		if ( backEnd.currentEntity->e.reFlags & REFLAG_ZOMBIEFX ) {
1051 			RB_ZombieFX( 0, drawSurf, oldNumVerts, oldNumIndex );
1052 		} else if ( backEnd.currentEntity->e.reFlags & REFLAG_ZOMBIEFX2 )     {
1053 			RB_ZombieFX( 1, drawSurf, oldNumVerts, oldNumIndex );
1054 		}
1055 	}
1056 
1057 	// draw the contents of the last shader batch
1058 	if ( oldShader != NULL ) {
1059 // GR - pass tessellation flag to the shader command
1060 //		make sure to use oldAtiTess!!!
1061 		tess.ATI_tess = ( oldAtiTess == ATI_TESS_TRUFORM );
1062 
1063 		RB_EndSurface();
1064 	}
1065 
1066 	// go back to the world modelview matrix
1067 	backEnd.currentEntity = &tr.worldEntity;
1068 	backEnd.refdef.floatTime = originalTime;
1069 	backEnd.or = backEnd.viewParms.world;
1070 	R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or );
1071 
1072 	qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
1073 	if ( depthRange ) {
1074 		qglDepthRange( 0, 1 );
1075 	}
1076 
1077 	if (r_drawSun->integer) {
1078 		RB_DrawSun(0.2, tr.sunShader);
1079 	}
1080 
1081 	// darken down any stencil shadows
1082 	RB_ShadowFinish();
1083 
1084 	// add light flares on lights that aren't obscured
1085 	RB_RenderFlares();
1086 }
1087 
1088 
1089 /*
1090 ============================================================================
1091 
1092 RENDER BACK END FUNCTIONS
1093 
1094 ============================================================================
1095 */
1096 
1097 /*
1098 ================
1099 RB_SetGL2D
1100 
1101 ================
1102 */
RB_SetGL2D(void)1103 void    RB_SetGL2D( void ) {
1104 	backEnd.projection2D = qtrue;
1105 
1106 	// set 2D virtual screen size
1107 	qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight );
1108 	qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight );
1109 	qglMatrixMode( GL_PROJECTION );
1110 	qglLoadIdentity();
1111 	qglOrtho( 0, glConfig.vidWidth, glConfig.vidHeight, 0, 0, 1 );
1112 	qglMatrixMode( GL_MODELVIEW );
1113 	qglLoadIdentity();
1114 
1115 	GL_State( GLS_DEPTHTEST_DISABLE |
1116 			  GLS_SRCBLEND_SRC_ALPHA |
1117 			  GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
1118 
1119 	qglDisable( GL_FOG ); //----(SA)	added
1120 
1121 	GL_Cull( CT_TWO_SIDED );
1122 	qglDisable( GL_CLIP_PLANE0 );
1123 
1124 	// set time for 2D shaders
1125 	backEnd.refdef.time = ri.Milliseconds();
1126 	backEnd.refdef.floatTime = backEnd.refdef.time * 0.001;
1127 }
1128 
1129 
1130 /*
1131 =============
1132 RE_StretchRaw
1133 
1134 FIXME: not exactly backend
1135 Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle.
1136 Used for cinematics.
1137 =============
1138 */
RE_StretchRaw(int x,int y,int w,int h,int cols,int rows,const byte * data,int client,qboolean dirty)1139 void RE_StretchRaw( int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty ) {
1140 	int i, j;
1141 	int start, end;
1142 
1143 	if ( !tr.registered ) {
1144 		return;
1145 	}
1146 	R_IssuePendingRenderCommands();
1147 
1148 	if ( tess.numIndexes ) {
1149 		RB_EndSurface();
1150 	}
1151 
1152 	// we definately want to sync every frame for the cinematics
1153 	qglFinish();
1154 
1155 	start = 0;
1156 	if ( r_speeds->integer ) {
1157 		start = ri.Milliseconds();
1158 	}
1159 
1160 	// make sure rows and cols are powers of 2
1161 	for ( i = 0 ; ( 1 << i ) < cols ; i++ ) {
1162 	}
1163 	for ( j = 0 ; ( 1 << j ) < rows ; j++ ) {
1164 	}
1165 	if ( ( 1 << i ) != cols || ( 1 << j ) != rows ) {
1166 		ri.Error( ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows );
1167 	}
1168 
1169 	RE_UploadCinematic (w, h, cols, rows, data, client, dirty);
1170 	GL_Bind( tr.scratchImage[client] );
1171 
1172 	if ( r_speeds->integer ) {
1173 		end = ri.Milliseconds();
1174 		ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start );
1175 	}
1176 
1177 	RB_SetGL2D();
1178 
1179 	qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight );
1180 
1181 #ifdef USE_OPENGLES
1182 	GLfloat tex[] = {
1183 	 0.5f / cols,  0.5f / rows,
1184 	 ( cols - 0.5f ) / cols ,  0.5f / rows,
1185 	 ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows,
1186 	 0.5f / cols, ( rows - 0.5f ) / rows };
1187 	GLfloat vtx[] = {
1188 	 x, y,
1189 	 x+w, y,
1190 	 x+w, y+h,
1191 	 x, y+h };
1192 	GLboolean text = qglIsEnabled(GL_TEXTURE_COORD_ARRAY);
1193 	GLboolean glcol = qglIsEnabled(GL_COLOR_ARRAY);
1194 	if (glcol)
1195 		qglDisableClientState(GL_COLOR_ARRAY);
1196 	if (!text)
1197 		qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
1198 	qglTexCoordPointer( 2, GL_FLOAT, 0, tex );
1199 	qglVertexPointer  ( 2, GL_FLOAT, 0, vtx );
1200 	qglDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
1201 	if (!text)
1202 		qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
1203 	if (glcol)
1204 		qglEnableClientState(GL_COLOR_ARRAY);
1205 #else
1206 	qglBegin( GL_QUADS );
1207 	qglTexCoord2f( 0.5f / cols,  0.5f / rows );
1208 	qglVertex2f( x, y );
1209 	qglTexCoord2f( ( cols - 0.5f ) / cols,  0.5f / rows );
1210 	qglVertex2f( x + w, y );
1211 	qglTexCoord2f( ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows );
1212 	qglVertex2f( x + w, y + h );
1213 	qglTexCoord2f( 0.5f / cols, ( rows - 0.5f ) / rows );
1214 	qglVertex2f( x, y + h );
1215 	qglEnd();
1216 #endif
1217 }
1218 
1219 
RE_UploadCinematic(int w,int h,int cols,int rows,const byte * data,int client,qboolean dirty)1220 void RE_UploadCinematic( int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty ) {
1221 
1222 	GL_Bind( tr.scratchImage[client] );
1223 
1224 	// if the scratchImage isn't in the format we want, specify it as a new texture
1225 	if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) {
1226 		tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols;
1227 		tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows;
1228 #ifdef USE_OPENGLES
1229 		qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
1230 #else
1231 		qglTexImage2D( GL_TEXTURE_2D, 0, 3, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
1232 #endif
1233 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1234 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1235 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1236 		qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1237 	} else {
1238 		if ( dirty ) {
1239 			// otherwise, just subimage upload it so that drivers can tell we are going to be changing
1240 			// it and don't try and do a texture compression
1241 			qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
1242 		}
1243 	}
1244 }
1245 
1246 
1247 /*
1248 =============
1249 RB_SetColor
1250 
1251 =============
1252 */
RB_SetColor(const void * data)1253 const void  *RB_SetColor( const void *data ) {
1254 	const setColorCommand_t *cmd;
1255 
1256 	cmd = (const setColorCommand_t *)data;
1257 
1258 	backEnd.color2D[0] = cmd->color[0] * 255;
1259 	backEnd.color2D[1] = cmd->color[1] * 255;
1260 	backEnd.color2D[2] = cmd->color[2] * 255;
1261 	backEnd.color2D[3] = cmd->color[3] * 255;
1262 
1263 	return (const void *)( cmd + 1 );
1264 }
1265 
1266 /*
1267 =============
1268 RB_StretchPic
1269 =============
1270 */
RB_StretchPic(const void * data)1271 const void *RB_StretchPic( const void *data ) {
1272 	const stretchPicCommand_t   *cmd;
1273 	shader_t *shader;
1274 	int numVerts, numIndexes;
1275 
1276 	cmd = (const stretchPicCommand_t *)data;
1277 
1278 	if ( !backEnd.projection2D ) {
1279 		RB_SetGL2D();
1280 	}
1281 
1282 	shader = cmd->shader;
1283 	if ( shader != tess.shader ) {
1284 		if ( tess.numIndexes ) {
1285 			RB_EndSurface();
1286 		}
1287 		backEnd.currentEntity = &backEnd.entity2D;
1288 		RB_BeginSurface( shader, 0 );
1289 	}
1290 
1291 	RB_CHECKOVERFLOW( 4, 6 );
1292 	numVerts = tess.numVertexes;
1293 	numIndexes = tess.numIndexes;
1294 
1295 	tess.numVertexes += 4;
1296 	tess.numIndexes += 6;
1297 
1298 	tess.indexes[ numIndexes ] = numVerts + 3;
1299 	tess.indexes[ numIndexes + 1 ] = numVerts + 0;
1300 	tess.indexes[ numIndexes + 2 ] = numVerts + 2;
1301 	tess.indexes[ numIndexes + 3 ] = numVerts + 2;
1302 	tess.indexes[ numIndexes + 4 ] = numVerts + 0;
1303 	tess.indexes[ numIndexes + 5 ] = numVerts + 1;
1304 
1305 	*(int *)tess.vertexColors[ numVerts ] =
1306 		*(int *)tess.vertexColors[ numVerts + 1 ] =
1307 			*(int *)tess.vertexColors[ numVerts + 2 ] =
1308 				*(int *)tess.vertexColors[ numVerts + 3 ] = *(int *)backEnd.color2D;
1309 
1310 	tess.xyz[ numVerts ][0] = cmd->x;
1311 	tess.xyz[ numVerts ][1] = cmd->y;
1312 	tess.xyz[ numVerts ][2] = 0;
1313 
1314 	tess.texCoords[ numVerts ][0][0] = cmd->s1;
1315 	tess.texCoords[ numVerts ][0][1] = cmd->t1;
1316 
1317 	tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w;
1318 	tess.xyz[ numVerts + 1 ][1] = cmd->y;
1319 	tess.xyz[ numVerts + 1 ][2] = 0;
1320 
1321 	tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2;
1322 	tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1;
1323 
1324 	tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w;
1325 	tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h;
1326 	tess.xyz[ numVerts + 2 ][2] = 0;
1327 
1328 	tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2;
1329 	tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2;
1330 
1331 	tess.xyz[ numVerts + 3 ][0] = cmd->x;
1332 	tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h;
1333 	tess.xyz[ numVerts + 3 ][2] = 0;
1334 
1335 	tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1;
1336 	tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2;
1337 
1338 	return (const void *)( cmd + 1 );
1339 }
1340 
1341 /*
1342 ==============
1343 RB_StretchPicGradient
1344 ==============
1345 */
RB_StretchPicGradient(const void * data)1346 const void *RB_StretchPicGradient( const void *data ) {
1347 	const stretchPicCommand_t   *cmd;
1348 	shader_t *shader;
1349 	int numVerts, numIndexes;
1350 
1351 	cmd = (const stretchPicCommand_t *)data;
1352 
1353 	if ( !backEnd.projection2D ) {
1354 		RB_SetGL2D();
1355 	}
1356 
1357 	shader = cmd->shader;
1358 	if ( shader != tess.shader ) {
1359 		if ( tess.numIndexes ) {
1360 			RB_EndSurface();
1361 		}
1362 		backEnd.currentEntity = &backEnd.entity2D;
1363 		RB_BeginSurface( shader, 0 );
1364 	}
1365 
1366 	RB_CHECKOVERFLOW( 4, 6 );
1367 	numVerts = tess.numVertexes;
1368 	numIndexes = tess.numIndexes;
1369 
1370 	tess.numVertexes += 4;
1371 	tess.numIndexes += 6;
1372 
1373 	tess.indexes[ numIndexes ] = numVerts + 3;
1374 	tess.indexes[ numIndexes + 1 ] = numVerts + 0;
1375 	tess.indexes[ numIndexes + 2 ] = numVerts + 2;
1376 	tess.indexes[ numIndexes + 3 ] = numVerts + 2;
1377 	tess.indexes[ numIndexes + 4 ] = numVerts + 0;
1378 	tess.indexes[ numIndexes + 5 ] = numVerts + 1;
1379 
1380 //	*(int *)tess.vertexColors[ numVerts ] =
1381 //		*(int *)tess.vertexColors[ numVerts + 1 ] =
1382 //		*(int *)tess.vertexColors[ numVerts + 2 ] =
1383 //		*(int *)tess.vertexColors[ numVerts + 3 ] = *(int *)backEnd.color2D;
1384 
1385 	*(int *)tess.vertexColors[ numVerts ] =
1386 		*(int *)tess.vertexColors[ numVerts + 1 ] = *(int *)backEnd.color2D;
1387 
1388 	*(int *)tess.vertexColors[ numVerts + 2 ] =
1389 		*(int *)tess.vertexColors[ numVerts + 3 ] = *(int *)cmd->gradientColor;
1390 
1391 	tess.xyz[ numVerts ][0] = cmd->x;
1392 	tess.xyz[ numVerts ][1] = cmd->y;
1393 	tess.xyz[ numVerts ][2] = 0;
1394 
1395 	tess.texCoords[ numVerts ][0][0] = cmd->s1;
1396 	tess.texCoords[ numVerts ][0][1] = cmd->t1;
1397 
1398 	tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w;
1399 	tess.xyz[ numVerts + 1 ][1] = cmd->y;
1400 	tess.xyz[ numVerts + 1 ][2] = 0;
1401 
1402 	tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2;
1403 	tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1;
1404 
1405 	tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w;
1406 	tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h;
1407 	tess.xyz[ numVerts + 2 ][2] = 0;
1408 
1409 	tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2;
1410 	tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2;
1411 
1412 	tess.xyz[ numVerts + 3 ][0] = cmd->x;
1413 	tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h;
1414 	tess.xyz[ numVerts + 3 ][2] = 0;
1415 
1416 	tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1;
1417 	tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2;
1418 
1419 	return (const void *)( cmd + 1 );
1420 }
1421 
1422 
1423 /*
1424 =============
1425 RB_DrawSurfs
1426 
1427 =============
1428 */
RB_DrawSurfs(const void * data)1429 const void  *RB_DrawSurfs( const void *data ) {
1430 	const drawSurfsCommand_t    *cmd;
1431 
1432 	// finish any 2D drawing if needed
1433 	if ( tess.numIndexes ) {
1434 		RB_EndSurface();
1435 	}
1436 
1437 	cmd = (const drawSurfsCommand_t *)data;
1438 
1439 	backEnd.refdef = cmd->refdef;
1440 	backEnd.viewParms = cmd->viewParms;
1441 
1442 #ifdef USE_BLOOM
1443 	//TODO Maybe check for rdf_noworld stuff but q3mme has full 3d ui
1444 	backEnd.doneSurfaces = qtrue;
1445 #endif
1446 	RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs );
1447 
1448 	return (const void *)( cmd + 1 );
1449 }
1450 
1451 
1452 /*
1453 =============
1454 RB_DrawBuffer
1455 
1456 =============
1457 */
RB_DrawBuffer(const void * data)1458 const void  *RB_DrawBuffer( const void *data ) {
1459 	const drawBufferCommand_t   *cmd;
1460 
1461 	cmd = (const drawBufferCommand_t *)data;
1462 
1463 #ifndef USE_OPENGLES
1464 	qglDrawBuffer( cmd->buffer );
1465 #endif
1466 
1467 	// clear screen for debugging
1468 	if ( r_clear->integer ) {
1469 		qglClearColor( 1, 0, 0.5, 1 );
1470 		qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1471 	}
1472 
1473 	return (const void *)( cmd + 1 );
1474 }
1475 
1476 /*
1477 ===============
1478 RB_ShowImages
1479 
1480 Draw all the images to the screen, on top of whatever
1481 was there.  This is used to test for texture thrashing.
1482 
1483 Also called by RE_EndRegistration
1484 ===============
1485 */
RB_ShowImages(void)1486 void RB_ShowImages( void ) {
1487 	int i;
1488 	image_t *image;
1489 	float x, y, w, h;
1490 	int start, end;
1491 
1492 	if ( !backEnd.projection2D ) {
1493 		RB_SetGL2D();
1494 	}
1495 
1496 	qglClear( GL_COLOR_BUFFER_BIT );
1497 
1498 	qglFinish();
1499 
1500 
1501 	start = ri.Milliseconds();
1502 
1503 	for ( i = 0 ; i < tr.numImages ; i++ ) {
1504 		image = tr.images[i];
1505 
1506 		w = glConfig.vidWidth / 40;
1507 		h = glConfig.vidHeight / 30;
1508 
1509 		x = i % 40 * w;
1510 		y = i / 30 * h;
1511 
1512 		// show in proportional size in mode 2
1513 		if ( r_showImages->integer == 2 ) {
1514 			w *= image->uploadWidth / 512.0f;
1515 			h *= image->uploadHeight / 512.0f;
1516 		}
1517 
1518 #ifdef USE_OPENGLES
1519 		GLfloat tex[] = {
1520 		 0, 0,
1521 		 1, 0,
1522 		 1, 1,
1523 		 0, 1 };
1524 		GLfloat vtx[] = {
1525 		 x, y,
1526 		 x + w, y,
1527 		 x + w, y + h,
1528 		 x, y + h };
1529 		GLboolean text = qglIsEnabled(GL_TEXTURE_COORD_ARRAY);
1530 		GLboolean glcol = qglIsEnabled(GL_COLOR_ARRAY);
1531 		if (glcol)
1532 			qglDisableClientState(GL_COLOR_ARRAY);
1533 		if (!text)
1534 			qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
1535 		qglTexCoordPointer( 2, GL_FLOAT, 0, tex );
1536 		qglVertexPointer  ( 2, GL_FLOAT, 0, vtx );
1537 		qglDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
1538 		if (glcol)
1539 			qglEnableClientState(GL_COLOR_ARRAY);
1540 		if (!text)
1541 			qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
1542 #else
1543 		GL_Bind( image );
1544 		qglBegin( GL_QUADS );
1545 		qglTexCoord2f( 0, 0 );
1546 		qglVertex2f( x, y );
1547 		qglTexCoord2f( 1, 0 );
1548 		qglVertex2f( x + w, y );
1549 		qglTexCoord2f( 1, 1 );
1550 		qglVertex2f( x + w, y + h );
1551 		qglTexCoord2f( 0, 1 );
1552 		qglVertex2f( x, y + h );
1553 		qglEnd();
1554 #endif
1555 	}
1556 
1557 	qglFinish();
1558 
1559 	end = ri.Milliseconds();
1560 	ri.Printf( PRINT_ALL, "%i msec to draw all images\n", end - start );
1561 
1562 }
1563 
1564 /*
1565 =============
1566 RB_ColorMask
1567 
1568 =============
1569 */
RB_ColorMask(const void * data)1570 const void *RB_ColorMask(const void *data)
1571 {
1572 	const colorMaskCommand_t *cmd = data;
1573 
1574 	qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]);
1575 
1576 	return (const void *)(cmd + 1);
1577 }
1578 
1579 /*
1580 =============
1581 RB_ClearDepth
1582 
1583 =============
1584 */
RB_ClearDepth(const void * data)1585 const void *RB_ClearDepth(const void *data)
1586 {
1587 	const clearDepthCommand_t *cmd = data;
1588 
1589 	if(tess.numIndexes)
1590 		RB_EndSurface();
1591 
1592 	// texture swapping test
1593 	if (r_showImages->integer)
1594 		RB_ShowImages();
1595 
1596 	qglClear(GL_DEPTH_BUFFER_BIT);
1597 
1598 	return (const void *)(cmd + 1);
1599 }
1600 
1601 /*
1602 =============
1603 RB_SwapBuffers
1604 
1605 =============
1606 */
RB_SwapBuffers(const void * data)1607 const void  *RB_SwapBuffers( const void *data ) {
1608 	const swapBuffersCommand_t  *cmd;
1609 
1610 	// finish any 2D drawing if needed
1611 	if ( tess.numIndexes ) {
1612 		RB_EndSurface();
1613 	}
1614 
1615 	// texture swapping test
1616 	if ( r_showImages->integer ) {
1617 		RB_ShowImages();
1618 	}
1619 
1620 	cmd = (const swapBuffersCommand_t *)data;
1621 
1622 	// we measure overdraw by reading back the stencil buffer and
1623 	// counting up the number of increments that have happened
1624 #ifndef USE_OPENGLES
1625 	if ( r_measureOverdraw->integer ) {
1626 		int i;
1627 		long sum = 0;
1628 		unsigned char *stencilReadback;
1629 
1630 		stencilReadback = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight );
1631 		qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback );
1632 
1633 		for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) {
1634 			sum += stencilReadback[i];
1635 		}
1636 
1637 		backEnd.pc.c_overDraw += sum;
1638 		ri.Hunk_FreeTempMemory( stencilReadback );
1639 	}
1640 #endif
1641 
1642 
1643 	if ( !glState.finishCalled ) {
1644 		qglFinish();
1645 	}
1646 
1647 	GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" );
1648 
1649 	GLimp_EndFrame();
1650 
1651 	backEnd.projection2D = qfalse;
1652 
1653 #ifdef USE_BLOOM
1654 	backEnd.doneBloom = qfalse;
1655 	backEnd.doneSurfaces = qfalse;
1656 #endif
1657 
1658 	return (const void *)( cmd + 1 );
1659 }
1660 
1661 /*
1662 ====================
1663 RB_ExecuteRenderCommands
1664 ====================
1665 */
RB_ExecuteRenderCommands(const void * data)1666 void RB_ExecuteRenderCommands( const void *data ) {
1667 	int t1, t2;
1668 
1669 	t1 = ri.Milliseconds();
1670 
1671 	while ( 1 ) {
1672 		data = PADP(data, sizeof(void *));
1673 
1674 		switch ( *(const int *)data ) {
1675 		case RC_SET_COLOR:
1676 			data = RB_SetColor( data );
1677 			break;
1678 		case RC_STRETCH_PIC:
1679 #ifdef USE_BLOOM
1680 			//Check if it's time for BLOOM!
1681 			R_BloomScreen();
1682 #endif
1683 			data = RB_StretchPic( data );
1684 			break;
1685 		case RC_STRETCH_PIC_GRADIENT:
1686 #ifdef USE_BLOOM
1687 			//Check if it's time for BLOOM!
1688 			R_BloomScreen();
1689 #endif
1690 			data = RB_StretchPicGradient( data );
1691 			break;
1692 		case RC_DRAW_SURFS:
1693 			data = RB_DrawSurfs( data );
1694 			break;
1695 		case RC_DRAW_BUFFER:
1696 			data = RB_DrawBuffer( data );
1697 			break;
1698 		case RC_SWAP_BUFFERS:
1699 #ifdef USE_BLOOM
1700 			//Check if it's time for BLOOM!
1701 			R_BloomScreen();
1702 #endif
1703 			data = RB_SwapBuffers( data );
1704 			break;
1705 		case RC_SCREENSHOT:
1706 			data = RB_TakeScreenshotCmd( data );
1707 			break;
1708 		case RC_VIDEOFRAME:
1709 			data = RB_TakeVideoFrameCmd( data );
1710 			break;
1711 		case RC_COLORMASK:
1712 			data = RB_ColorMask(data);
1713 			break;
1714 		case RC_CLEARDEPTH:
1715 			data = RB_ClearDepth(data);
1716 			break;
1717 		case RC_END_OF_LIST:
1718 		default:
1719 			// stop rendering
1720 			t2 = ri.Milliseconds();
1721 			backEnd.pc.msec = t2 - t1;
1722 			return;
1723 		}
1724 	}
1725 
1726 }
1727 
1728