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