1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 #include "sys/platform.h"
29 #include "idlib/containers/List.h"
30 #include "framework/EventLoop.h"
31 #include "framework/Session.h"
32 #include "framework/DemoFile.h"
33 #include "renderer/ModelManager.h"
34 #include "renderer/Material.h"
35 #include "renderer/GuiModel.h"
36 #include "renderer/VertexCache.h"
37 #include "renderer/RenderWorld_local.h"
38 
39 #include "renderer/tr_local.h"
40 
41 idRenderSystemLocal	tr;
42 idRenderSystem	*renderSystem = &tr;
43 
44 
45 /*
46 =====================
47 R_PerformanceCounters
48 
49 This prints both front and back end counters, so it should
50 only be called when the back end thread is idle.
51 =====================
52 */
R_PerformanceCounters(void)53 static void R_PerformanceCounters( void ) {
54 	if ( r_showPrimitives.GetInteger() != 0 ) {
55 
56 		float megaBytes = globalImages->SumOfUsedImages() / ( 1024*1024.0 );
57 
58 		if ( r_showPrimitives.GetInteger() > 1 ) {
59 			common->Printf( "v:%i ds:%i t:%i/%i v:%i/%i st:%i sv:%i image:%5.1f MB\n",
60 				tr.pc.c_numViews,
61 				backEnd.pc.c_drawElements + backEnd.pc.c_shadowElements,
62 				backEnd.pc.c_drawIndexes / 3,
63 				( backEnd.pc.c_drawIndexes - backEnd.pc.c_drawRefIndexes ) / 3,
64 				backEnd.pc.c_drawVertexes,
65 				( backEnd.pc.c_drawVertexes - backEnd.pc.c_drawRefVertexes ),
66 				backEnd.pc.c_shadowIndexes / 3,
67 				backEnd.pc.c_shadowVertexes,
68 				megaBytes
69 				);
70 		} else {
71 			common->Printf( "views:%i draws:%i tris:%i (shdw:%i) (vbo:%i) image:%5.1f MB\n",
72 				tr.pc.c_numViews,
73 				backEnd.pc.c_drawElements + backEnd.pc.c_shadowElements,
74 				( backEnd.pc.c_drawIndexes + backEnd.pc.c_shadowIndexes ) / 3,
75 				backEnd.pc.c_shadowIndexes / 3,
76 				backEnd.pc.c_vboIndexes / 3,
77 				megaBytes
78 				);
79 		}
80 	}
81 
82 	if ( r_showDynamic.GetBool() ) {
83 		common->Printf( "callback:%i md5:%i dfrmVerts:%i dfrmTris:%i tangTris:%i guis:%i\n",
84 			tr.pc.c_entityDefCallbacks,
85 			tr.pc.c_generateMd5,
86 			tr.pc.c_deformedVerts,
87 			tr.pc.c_deformedIndexes/3,
88 			tr.pc.c_tangentIndexes/3,
89 			tr.pc.c_guiSurfs
90 			);
91 	}
92 
93 	if ( r_showCull.GetBool() ) {
94 		common->Printf( "%i sin %i sclip  %i sout %i bin %i bout\n",
95 			tr.pc.c_sphere_cull_in, tr.pc.c_sphere_cull_clip, tr.pc.c_sphere_cull_out,
96 			tr.pc.c_box_cull_in, tr.pc.c_box_cull_out );
97 	}
98 
99 	if ( r_showAlloc.GetBool() ) {
100 		common->Printf( "alloc:%i free:%i\n", tr.pc.c_alloc, tr.pc.c_free );
101 	}
102 
103 	if ( r_showInteractions.GetBool() ) {
104 		common->Printf( "createInteractions:%i createLightTris:%i createShadowVolumes:%i\n",
105 			tr.pc.c_createInteractions, tr.pc.c_createLightTris, tr.pc.c_createShadowVolumes );
106 	}
107 	if ( r_showDefs.GetBool() ) {
108 		common->Printf( "viewEntities:%i  shadowEntities:%i  viewLights:%i\n", tr.pc.c_visibleViewEntities,
109 			tr.pc.c_shadowViewEntities, tr.pc.c_viewLights );
110 	}
111 	if ( r_showUpdates.GetBool() ) {
112 		common->Printf( "entityUpdates:%i  entityRefs:%i  lightUpdates:%i  lightRefs:%i\n",
113 			tr.pc.c_entityUpdates, tr.pc.c_entityReferences,
114 			tr.pc.c_lightUpdates, tr.pc.c_lightReferences );
115 	}
116 	if ( r_showMemory.GetBool() ) {
117 		int	m1 = frameData ? frameData->memoryHighwater : 0;
118 		common->Printf( "frameData: %i (%i)\n", R_CountFrameData(), m1 );
119 	}
120 	if ( r_showLightScale.GetBool() ) {
121 		common->Printf( "lightScale: %f\n", backEnd.pc.maxLightValue );
122 	}
123 
124 	memset( &tr.pc, 0, sizeof( tr.pc ) );
125 	memset( &backEnd.pc, 0, sizeof( backEnd.pc ) );
126 }
127 
128 
129 
130 /*
131 ====================
132 R_IssueRenderCommands
133 
134 Called by R_EndFrame each frame
135 ====================
136 */
R_IssueRenderCommands(void)137 static void R_IssueRenderCommands( void ) {
138 	if ( frameData->cmdHead->commandId == RC_NOP
139 		&& !frameData->cmdHead->next ) {
140 		// nothing to issue
141 		return;
142 	}
143 
144 	// r_skipBackEnd allows the entire time of the back end
145 	// to be removed from performance measurements, although
146 	// nothing will be drawn to the screen.  If the prints
147 	// are going to a file, or r_skipBackEnd is later disabled,
148 	// usefull data can be received.
149 
150 	// r_skipRender is usually more usefull, because it will still
151 	// draw 2D graphics
152 	if ( !r_skipBackEnd.GetBool() ) {
153 		RB_ExecuteBackEndCommands( frameData->cmdHead );
154 	}
155 
156 	R_ClearCommandChain();
157 }
158 
159 /*
160 ============
161 R_GetCommandBuffer
162 
163 Returns memory for a command buffer (stretchPicCommand_t,
164 drawSurfsCommand_t, etc) and links it to the end of the
165 current command chain.
166 ============
167 */
R_GetCommandBuffer(int bytes)168 void *R_GetCommandBuffer( int bytes ) {
169 	emptyCommand_t	*cmd;
170 
171 	cmd = (emptyCommand_t *)R_FrameAlloc( bytes );
172 	cmd->next = NULL;
173 	frameData->cmdTail->next = &cmd->commandId;
174 	frameData->cmdTail = cmd;
175 
176 	return (void *)cmd;
177 }
178 
179 
180 /*
181 ====================
182 R_ClearCommandChain
183 
184 Called after every buffer submission
185 and by R_ToggleSmpFrame
186 ====================
187 */
R_ClearCommandChain(void)188 void R_ClearCommandChain( void ) {
189 	// clear the command chain
190 	frameData->cmdHead = frameData->cmdTail = (emptyCommand_t *)R_FrameAlloc( sizeof( *frameData->cmdHead ) );
191 	frameData->cmdHead->commandId = RC_NOP;
192 	frameData->cmdHead->next = NULL;
193 }
194 
195 /*
196 =================
197 R_ViewStatistics
198 =================
199 */
R_ViewStatistics(viewDef_t * parms)200 static void R_ViewStatistics( viewDef_t *parms ) {
201 	// report statistics about this view
202 	if ( !r_showSurfaces.GetBool() ) {
203 		return;
204 	}
205 	common->Printf( "view:%p surfs:%i\n", parms, parms->numDrawSurfs );
206 }
207 
208 /*
209 =============
210 R_AddDrawViewCmd
211 
212 This is the main 3D rendering command.  A single scene may
213 have multiple views if a mirror, portal, or dynamic texture is present.
214 =============
215 */
R_AddDrawViewCmd(viewDef_t * parms)216 void	R_AddDrawViewCmd( viewDef_t *parms ) {
217 	drawSurfsCommand_t	*cmd;
218 
219 	cmd = (drawSurfsCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) );
220 	cmd->commandId = RC_DRAW_VIEW;
221 
222 	cmd->viewDef = parms;
223 
224 	if ( parms->viewEntitys ) {
225 		// save the command for r_lockSurfaces debugging
226 		tr.lockSurfacesCmd = *cmd;
227 	}
228 
229 	tr.pc.c_numViews++;
230 
231 	R_ViewStatistics( parms );
232 }
233 
234 
235 //=================================================================================
236 
237 
238 /*
239 ======================
240 R_LockSurfaceScene
241 
242 r_lockSurfaces allows a developer to move around
243 without changing the composition of the scene, including
244 culling.  The only thing that is modified is the
245 view position and axis, no front end work is done at all
246 
247 
248 Add the stored off command again, so the new rendering will use EXACTLY
249 the same surfaces, including all the culling, even though the transformation
250 matricies have been changed.  This allow the culling tightness to be
251 evaluated interactively.
252 ======================
253 */
R_LockSurfaceScene(viewDef_t * parms)254 void R_LockSurfaceScene( viewDef_t *parms ) {
255 	drawSurfsCommand_t	*cmd;
256 	viewEntity_t			*vModel;
257 
258 	// set the matrix for world space to eye space
259 	R_SetViewMatrix( parms );
260 	tr.lockSurfacesCmd.viewDef->worldSpace = parms->worldSpace;
261 
262 	// update the view origin and axis, and all
263 	// the entity matricies
264 	for( vModel = tr.lockSurfacesCmd.viewDef->viewEntitys ; vModel ; vModel = vModel->next ) {
265 		myGlMultMatrix( vModel->modelMatrix,
266 			tr.lockSurfacesCmd.viewDef->worldSpace.modelViewMatrix,
267 			vModel->modelViewMatrix );
268 	}
269 
270 	// add the stored off surface commands again
271 	cmd = (drawSurfsCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) );
272 	*cmd = tr.lockSurfacesCmd;
273 }
274 
275 /*
276 =============
277 R_CheckCvars
278 
279 See if some cvars that we watch have changed
280 =============
281 */
R_CheckCvars(void)282 static void R_CheckCvars( void ) {
283 	globalImages->CheckCvars();
284 
285 	// gamma stuff
286 	if ( r_gamma.IsModified() || r_brightness.IsModified() ) {
287 		r_gamma.ClearModified();
288 		r_brightness.ClearModified();
289 		R_SetColorMappings();
290 	}
291 }
292 
293 /*
294 =============
295 idRenderSystemLocal::idRenderSystemLocal
296 =============
297 */
idRenderSystemLocal(void)298 idRenderSystemLocal::idRenderSystemLocal( void ) {
299 	Clear();
300 }
301 
302 /*
303 =============
304 idRenderSystemLocal::~idRenderSystemLocal
305 =============
306 */
~idRenderSystemLocal(void)307 idRenderSystemLocal::~idRenderSystemLocal( void ) {
308 }
309 
310 /*
311 =============
312 SetColor
313 
314 This can be used to pass general information to the current material, not
315 just colors
316 =============
317 */
SetColor(const idVec4 & rgba)318 void idRenderSystemLocal::SetColor( const idVec4 &rgba ) {
319 	guiModel->SetColor( rgba[0], rgba[1], rgba[2], rgba[3] );
320 }
321 
322 
323 /*
324 =============
325 SetColor4
326 =============
327 */
SetColor4(float r,float g,float b,float a)328 void idRenderSystemLocal::SetColor4( float r, float g, float b, float a ) {
329 	guiModel->SetColor( r, g, b, a );
330 }
331 
332 /*
333 =============
334 DrawStretchPic
335 =============
336 */
DrawStretchPic(const idDrawVert * verts,const glIndex_t * indexes,int vertCount,int indexCount,const idMaterial * material,bool clip,float min_x,float min_y,float max_x,float max_y)337 void idRenderSystemLocal::DrawStretchPic( const idDrawVert *verts, const glIndex_t *indexes, int vertCount, int indexCount, const idMaterial *material,
338 									   bool clip, float min_x, float min_y, float max_x, float max_y ) {
339 	guiModel->DrawStretchPic( verts, indexes, vertCount, indexCount, material,
340 		clip, min_x, min_y, max_x, max_y );
341 }
342 
343 /*
344 =============
345 DrawStretchPic
346 
347 x/y/w/h are in the 0,0 to 640,480 range
348 =============
349 */
DrawStretchPic(float x,float y,float w,float h,float s1,float t1,float s2,float t2,const idMaterial * material)350 void idRenderSystemLocal::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *material ) {
351 	guiModel->DrawStretchPic( x, y, w, h, s1, t1, s2, t2, material );
352 }
353 
354 /*
355 =============
356 DrawStretchTri
357 
358 x/y/w/h are in the 0,0 to 640,480 range
359 =============
360 */
DrawStretchTri(idVec2 p1,idVec2 p2,idVec2 p3,idVec2 t1,idVec2 t2,idVec2 t3,const idMaterial * material)361 void idRenderSystemLocal::DrawStretchTri( idVec2 p1, idVec2 p2, idVec2 p3, idVec2 t1, idVec2 t2, idVec2 t3, const idMaterial *material ) {
362 	tr.guiModel->DrawStretchTri( p1, p2, p3, t1, t2, t3, material );
363 }
364 
365 /*
366 =============
367 GlobalToNormalizedDeviceCoordinates
368 =============
369 */
GlobalToNormalizedDeviceCoordinates(const idVec3 & global,idVec3 & ndc)370 void idRenderSystemLocal::GlobalToNormalizedDeviceCoordinates( const idVec3 &global, idVec3 &ndc ) {
371 	R_GlobalToNormalizedDeviceCoordinates( global, ndc );
372 }
373 
374 /*
375 =============
376 GlobalToNormalizedDeviceCoordinates
377 =============
378 */
GetGLSettings(int & width,int & height)379 void idRenderSystemLocal::GetGLSettings( int& width, int& height ) {
380 	width = glConfig.vidWidth;
381 	height = glConfig.vidHeight;
382 }
383 
384 /*
385 =====================
386 idRenderSystemLocal::DrawSmallChar
387 
388 small chars are drawn at native screen resolution
389 =====================
390 */
DrawSmallChar(int x,int y,int ch,const idMaterial * material)391 void idRenderSystemLocal::DrawSmallChar( int x, int y, int ch, const idMaterial *material ) {
392 	int row, col;
393 	float frow, fcol;
394 	float size;
395 
396 	ch &= 255;
397 
398 	if ( ch == ' ' ) {
399 		return;
400 	}
401 
402 	if ( y < -SMALLCHAR_HEIGHT ) {
403 		return;
404 	}
405 
406 	row = ch >> 4;
407 	col = ch & 15;
408 
409 	frow = row * 0.0625f;
410 	fcol = col * 0.0625f;
411 	size = 0.0625f;
412 
413 	DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,
414 					   fcol, frow,
415 					   fcol + size, frow + size,
416 					   material );
417 }
418 
419 /*
420 ==================
421 idRenderSystemLocal::DrawSmallString[Color]
422 
423 Draws a multi-colored string with a drop shadow, optionally forcing
424 to a fixed color.
425 
426 Coordinates are at 640 by 480 virtual resolution
427 ==================
428 */
DrawSmallStringExt(int x,int y,const char * string,const idVec4 & setColor,bool forceColor,const idMaterial * material)429 void idRenderSystemLocal::DrawSmallStringExt( int x, int y, const char *string, const idVec4 &setColor, bool forceColor, const idMaterial *material ) {
430 	idVec4		color;
431 	const unsigned char	*s;
432 	int			xx;
433 
434 	// draw the colored text
435 	s = (const unsigned char*)string;
436 	xx = x;
437 	SetColor( setColor );
438 	while ( *s ) {
439 		if ( idStr::IsColor( (const char*)s ) ) {
440 			if ( !forceColor ) {
441 				if ( *(s+1) == C_COLOR_DEFAULT ) {
442 					SetColor( setColor );
443 				} else {
444 					color = idStr::ColorForIndex( *(s+1) );
445 					color[3] = setColor[3];
446 					SetColor( color );
447 				}
448 			}
449 			s += 2;
450 			continue;
451 		}
452 		DrawSmallChar( xx, y, *s, material );
453 		xx += SMALLCHAR_WIDTH;
454 		s++;
455 	}
456 	SetColor( colorWhite );
457 }
458 
459 /*
460 =====================
461 idRenderSystemLocal::DrawBigChar
462 =====================
463 */
DrawBigChar(int x,int y,int ch,const idMaterial * material)464 void idRenderSystemLocal::DrawBigChar( int x, int y, int ch, const idMaterial *material ) {
465 	int row, col;
466 	float frow, fcol;
467 	float size;
468 
469 	ch &= 255;
470 
471 	if ( ch == ' ' ) {
472 		return;
473 	}
474 
475 	if ( y < -BIGCHAR_HEIGHT ) {
476 		return;
477 	}
478 
479 	row = ch >> 4;
480 	col = ch & 15;
481 
482 	frow = row * 0.0625f;
483 	fcol = col * 0.0625f;
484 	size = 0.0625f;
485 
486 	DrawStretchPic( x, y, BIGCHAR_WIDTH, BIGCHAR_HEIGHT,
487 					   fcol, frow,
488 					   fcol + size, frow + size,
489 					   material );
490 }
491 
492 /*
493 ==================
494 idRenderSystemLocal::DrawBigString[Color]
495 
496 Draws a multi-colored string with a drop shadow, optionally forcing
497 to a fixed color.
498 
499 Coordinates are at 640 by 480 virtual resolution
500 ==================
501 */
DrawBigStringExt(int x,int y,const char * string,const idVec4 & setColor,bool forceColor,const idMaterial * material)502 void idRenderSystemLocal::DrawBigStringExt( int x, int y, const char *string, const idVec4 &setColor, bool forceColor, const idMaterial *material ) {
503 	idVec4		color;
504 	const char	*s;
505 	int			xx;
506 
507 	// draw the colored text
508 	s = string;
509 	xx = x;
510 	SetColor( setColor );
511 	while ( *s ) {
512 		if ( idStr::IsColor( s ) ) {
513 			if ( !forceColor ) {
514 				if ( *(s+1) == C_COLOR_DEFAULT ) {
515 					SetColor( setColor );
516 				} else {
517 					color = idStr::ColorForIndex( *(s+1) );
518 					color[3] = setColor[3];
519 					SetColor( color );
520 				}
521 			}
522 			s += 2;
523 			continue;
524 		}
525 		DrawBigChar( xx, y, *s, material );
526 		xx += BIGCHAR_WIDTH;
527 		s++;
528 	}
529 	SetColor( colorWhite );
530 }
531 
532 //======================================================================================
533 
534 /*
535 ==================
536 SetBackEndRenderer
537 
538 Check for changes in the back end renderSystem, possibly invalidating cached data
539 ==================
540 */
SetBackEndRenderer()541 void idRenderSystemLocal::SetBackEndRenderer() {
542 	if ( !r_renderer.IsModified() ) {
543 		return;
544 	}
545 
546 	bool oldVPstate = backEndRendererHasVertexPrograms;
547 
548 	backEndRenderer = BE_BAD;
549 
550 	if ( idStr::Icmp( r_renderer.GetString(), "arb2" ) == 0 ) {
551 		if ( glConfig.allowARB2Path ) {
552 			backEndRenderer = BE_ARB2;
553 		}
554 	}
555 
556 	// fallback
557 	if ( backEndRenderer == BE_BAD ) {
558 		// choose the best
559 		if ( glConfig.allowARB2Path ) {
560 			backEndRenderer = BE_ARB2;
561 		}
562 	}
563 
564 	backEndRendererHasVertexPrograms = false;
565 	backEndRendererMaxLight = 1.0;
566 
567 	switch( backEndRenderer ) {
568 	case BE_ARB2:
569 		common->Printf( "using ARB2 renderSystem\n" );
570 		backEndRendererHasVertexPrograms = true;
571 		backEndRendererMaxLight = 999;
572 		break;
573 	default:
574 		common->FatalError( "SetbackEndRenderer: bad back end" );
575 	}
576 
577 	// clear the vertex cache if we are changing between
578 	// using vertex programs and not, because specular and
579 	// shadows will be different data
580 	if ( oldVPstate != backEndRendererHasVertexPrograms ) {
581 		vertexCache.PurgeAll();
582 		if ( primaryWorld ) {
583 			primaryWorld->FreeInteractions();
584 		}
585 	}
586 
587 	r_renderer.ClearModified();
588 }
589 
590 /*
591 ====================
592 BeginFrame
593 ====================
594 */
BeginFrame(int windowWidth,int windowHeight)595 void idRenderSystemLocal::BeginFrame( int windowWidth, int windowHeight ) {
596 	setBufferCommand_t	*cmd;
597 
598 	if ( !glConfig.isInitialized ) {
599 		return;
600 	}
601 
602 	// determine which back end we will use
603 	SetBackEndRenderer();
604 
605 	guiModel->Clear();
606 
607 	// for the larger-than-window tiled rendering screenshots
608 	if ( tiledViewport[0] ) {
609 		windowWidth = tiledViewport[0];
610 		windowHeight = tiledViewport[1];
611 	}
612 
613 	// DG: save the original size, so editors don't mess up the game viewport
614 	//     with their tiny (texture-preview etc) viewports.
615 	origWidth = glConfig.vidWidth;
616 	origHeight = glConfig.vidHeight;
617 
618 	glConfig.vidWidth = windowWidth;
619 	glConfig.vidHeight = windowHeight;
620 
621 	renderCrops[0].x = 0;
622 	renderCrops[0].y = 0;
623 	renderCrops[0].width = windowWidth;
624 	renderCrops[0].height = windowHeight;
625 	currentRenderCrop = 0;
626 
627 	// screenFraction is just for quickly testing fill rate limitations
628 	if ( r_screenFraction.GetInteger() != 100 ) {
629 		int	w = SCREEN_WIDTH * r_screenFraction.GetInteger() / 100.0f;
630 		int h = SCREEN_HEIGHT * r_screenFraction.GetInteger() / 100.0f;
631 		CropRenderSize( w, h );
632 	}
633 
634 
635 	// this is the ONLY place this is modified
636 	frameCount++;
637 
638 	// just in case we did a common->Error while this
639 	// was set
640 	guiRecursionLevel = 0;
641 
642 	// the first rendering will be used for commands like
643 	// screenshot, rather than a possible subsequent remote
644 	// or mirror render
645 //	primaryWorld = NULL;
646 
647 	// set the time for shader effects in 2D rendering
648 	frameShaderTime = eventLoop->Milliseconds() * 0.001;
649 
650 	//
651 	// draw buffer stuff
652 	//
653 	cmd = (setBufferCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) );
654 	cmd->commandId = RC_SET_BUFFER;
655 	cmd->frameCount = frameCount;
656 
657 	if ( r_frontBuffer.GetBool() ) {
658 		cmd->buffer = (int)GL_FRONT;
659 	} else {
660 		cmd->buffer = (int)GL_BACK;
661 	}
662 }
663 
WriteDemoPics()664 void idRenderSystemLocal::WriteDemoPics() {
665 	session->writeDemo->WriteInt( DS_RENDER );
666 	session->writeDemo->WriteInt( DC_GUI_MODEL );
667 	guiModel->WriteToDemo( session->writeDemo );
668 }
669 
DrawDemoPics()670 void idRenderSystemLocal::DrawDemoPics() {
671 	demoGuiModel->EmitFullScreen();
672 }
673 
674 /*
675 =============
676 EndFrame
677 
678 Returns the number of msec spent in the back end
679 =============
680 */
EndFrame(int * frontEndMsec,int * backEndMsec)681 void idRenderSystemLocal::EndFrame( int *frontEndMsec, int *backEndMsec ) {
682 	emptyCommand_t *cmd;
683 
684 	if ( !glConfig.isInitialized ) {
685 		return;
686 	}
687 
688 	// close any gui drawing
689 	guiModel->EmitFullScreen();
690 	guiModel->Clear();
691 
692 	// save out timing information
693 	if ( frontEndMsec ) {
694 		*frontEndMsec = pc.frontEndMsec;
695 	}
696 	if ( backEndMsec ) {
697 		*backEndMsec = backEnd.pc.msec;
698 	}
699 
700 	// print any other statistics and clear all of them
701 	R_PerformanceCounters();
702 
703 	// check for dynamic changes that require some initialization
704 	R_CheckCvars();
705 
706 	// check for errors
707 	GL_CheckErrors();
708 
709 	// add the swapbuffers command
710 	cmd = (emptyCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) );
711 	cmd->commandId = RC_SWAP_BUFFERS;
712 
713 	// start the back end up again with the new command list
714 	R_IssueRenderCommands();
715 
716 	// use the other buffers next frame, because another CPU
717 	// may still be rendering into the current buffers
718 	R_ToggleSmpFrame();
719 
720 	// we can now release the vertexes used this frame
721 	vertexCache.EndFrame();
722 
723 	if ( session->writeDemo ) {
724 		session->writeDemo->WriteInt( DS_RENDER );
725 		session->writeDemo->WriteInt( DC_END_FRAME );
726 		if ( r_showDemo.GetBool() ) {
727 			common->Printf( "write DC_END_FRAME\n" );
728 		}
729 	}
730 
731 	// DG: restore the original size that was set before BeginFrame() overwrote it
732 	//     with its function-arguments, so editors don't mess up our viewport.
733 	//     (unsure why/how this at least *kinda* worked in original Doom3,
734 	//      maybe glConfig.vidWidth/Height was reset if the window gained focus or sth)
735 	glConfig.vidWidth = origWidth;
736 	glConfig.vidHeight = origHeight;
737 }
738 
739 /*
740 =====================
741 RenderViewToViewport
742 
743 Converts from SCREEN_WIDTH / SCREEN_HEIGHT coordinates to current cropped pixel coordinates
744 =====================
745 */
RenderViewToViewport(const renderView_t * renderView,idScreenRect * viewport)746 void idRenderSystemLocal::RenderViewToViewport( const renderView_t *renderView, idScreenRect *viewport ) {
747 	renderCrop_t	*rc = &renderCrops[currentRenderCrop];
748 
749 	float wRatio = (float)rc->width / SCREEN_WIDTH;
750 	float hRatio = (float)rc->height / SCREEN_HEIGHT;
751 
752 	viewport->x1 = idMath::Ftoi( rc->x + renderView->x * wRatio );
753 	viewport->x2 = idMath::Ftoi( rc->x + floor( ( renderView->x + renderView->width ) * wRatio + 0.5f ) - 1 );
754 	viewport->y1 = idMath::Ftoi( ( rc->y + rc->height ) - floor( ( renderView->y + renderView->height ) * hRatio + 0.5f ) );
755 	viewport->y2 = idMath::Ftoi( ( rc->y + rc->height ) - floor( renderView->y * hRatio + 0.5f ) - 1 );
756 }
757 
RoundDownToPowerOfTwo(int v)758 static int RoundDownToPowerOfTwo( int v ) {
759 	int	i;
760 
761 	for ( i = 0 ; i < 20 ; i++ ) {
762 		if ( ( 1 << i ) == v ) {
763 			return v;
764 		}
765 		if ( ( 1 << i ) > v ) {
766 			return 1 << ( i-1 );
767 		}
768 	}
769 	return 1<<i;
770 }
771 
772 /*
773 ================
774 CropRenderSize
775 
776 This automatically halves sizes until it fits in the current window size,
777 so if you specify a power of two size for a texture copy, it may be shrunk
778 down, but still valid.
779 ================
780 */
CropRenderSize(int width,int height,bool makePowerOfTwo,bool forceDimensions)781 void	idRenderSystemLocal::CropRenderSize( int width, int height, bool makePowerOfTwo, bool forceDimensions ) {
782 	if ( !glConfig.isInitialized ) {
783 		return;
784 	}
785 
786 	// close any gui drawing before changing the size
787 	guiModel->EmitFullScreen();
788 	guiModel->Clear();
789 
790 	if ( width < 1 || height < 1 ) {
791 		common->Error( "CropRenderSize: bad sizes" );
792 	}
793 
794 	if ( session->writeDemo ) {
795 		session->writeDemo->WriteInt( DS_RENDER );
796 		session->writeDemo->WriteInt( DC_CROP_RENDER );
797 		session->writeDemo->WriteInt( width );
798 		session->writeDemo->WriteInt( height );
799 		session->writeDemo->WriteInt( makePowerOfTwo );
800 
801 		if ( r_showDemo.GetBool() ) {
802 			common->Printf( "write DC_CROP_RENDER\n" );
803 		}
804 	}
805 
806 	// convert from virtual SCREEN_WIDTH/SCREEN_HEIGHT coordinates to physical OpenGL pixels
807 	renderView_t renderView;
808 	renderView.x = 0;
809 	renderView.y = 0;
810 	renderView.width = width;
811 	renderView.height = height;
812 
813 	idScreenRect	r;
814 	RenderViewToViewport( &renderView, &r );
815 
816 	width = r.x2 - r.x1 + 1;
817 	height = r.y2 - r.y1 + 1;
818 
819 	if ( forceDimensions ) {
820 		// just give exactly what we ask for
821 		width = renderView.width;
822 		height = renderView.height;
823 	}
824 
825 	// if makePowerOfTwo, drop to next lower power of two after scaling to physical pixels
826 	if ( makePowerOfTwo ) {
827 		width = RoundDownToPowerOfTwo( width );
828 		height = RoundDownToPowerOfTwo( height );
829 		// FIXME: megascreenshots with offset viewports don't work right with this yet
830 	}
831 
832 	renderCrop_t	*rc = &renderCrops[currentRenderCrop];
833 
834 	// we might want to clip these to the crop window instead
835 	while ( width > glConfig.vidWidth ) {
836 		width >>= 1;
837 	}
838 	while ( height > glConfig.vidHeight ) {
839 		height >>= 1;
840 	}
841 
842 	if ( currentRenderCrop == MAX_RENDER_CROPS ) {
843 		common->Error( "idRenderSystemLocal::CropRenderSize: currentRenderCrop == MAX_RENDER_CROPS" );
844 	}
845 
846 	currentRenderCrop++;
847 
848 	rc = &renderCrops[currentRenderCrop];
849 
850 	rc->x = 0;
851 	rc->y = 0;
852 	rc->width = width;
853 	rc->height = height;
854 }
855 
856 /*
857 ================
858 UnCrop
859 ================
860 */
UnCrop()861 void idRenderSystemLocal::UnCrop() {
862 	if ( !glConfig.isInitialized ) {
863 		return;
864 	}
865 
866 	if ( currentRenderCrop < 1 ) {
867 		common->Error( "idRenderSystemLocal::UnCrop: currentRenderCrop < 1" );
868 	}
869 
870 	// close any gui drawing
871 	guiModel->EmitFullScreen();
872 	guiModel->Clear();
873 
874 	currentRenderCrop--;
875 
876 	if ( session->writeDemo ) {
877 		session->writeDemo->WriteInt( DS_RENDER );
878 		session->writeDemo->WriteInt( DC_UNCROP_RENDER );
879 
880 		if ( r_showDemo.GetBool() ) {
881 			common->Printf( "write DC_UNCROP\n" );
882 		}
883 	}
884 }
885 
886 /*
887 ================
888 CaptureRenderToImage
889 ================
890 */
CaptureRenderToImage(const char * imageName)891 void idRenderSystemLocal::CaptureRenderToImage( const char *imageName ) {
892 	if ( !glConfig.isInitialized ) {
893 		return;
894 	}
895 	guiModel->EmitFullScreen();
896 	guiModel->Clear();
897 
898 	if ( session->writeDemo ) {
899 		session->writeDemo->WriteInt( DS_RENDER );
900 		session->writeDemo->WriteInt( DC_CAPTURE_RENDER );
901 		session->writeDemo->WriteHashString( imageName );
902 
903 		if ( r_showDemo.GetBool() ) {
904 			common->Printf( "write DC_CAPTURE_RENDER: %s\n", imageName );
905 		}
906 	}
907 
908 	// look up the image before we create the render command, because it
909 	// may need to sync to create the image
910 	idImage	*image = globalImages->ImageFromFile(imageName, TF_DEFAULT, true, TR_REPEAT, TD_DEFAULT);
911 
912 	renderCrop_t *rc = &renderCrops[currentRenderCrop];
913 
914 	copyRenderCommand_t *cmd = (copyRenderCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) );
915 	cmd->commandId = RC_COPY_RENDER;
916 	cmd->x = rc->x;
917 	cmd->y = rc->y;
918 	cmd->imageWidth = rc->width;
919 	cmd->imageHeight = rc->height;
920 	cmd->image = image;
921 
922 	guiModel->Clear();
923 }
924 
925 /*
926 ==============
927 CaptureRenderToFile
928 
929 ==============
930 */
CaptureRenderToFile(const char * fileName,bool fixAlpha)931 void idRenderSystemLocal::CaptureRenderToFile( const char *fileName, bool fixAlpha ) {
932 	if ( !glConfig.isInitialized ) {
933 		return;
934 	}
935 
936 	renderCrop_t *rc = &renderCrops[currentRenderCrop];
937 
938 	guiModel->EmitFullScreen();
939 	guiModel->Clear();
940 	R_IssueRenderCommands();
941 
942 	qglReadBuffer( GL_BACK );
943 
944 	// include extra space for OpenGL padding to word boundaries
945 	int	c = ( rc->width + 3 ) * rc->height;
946 	byte *data = (byte *)R_StaticAlloc( c * 3 );
947 
948 	qglReadPixels( rc->x, rc->y, rc->width, rc->height, GL_RGB, GL_UNSIGNED_BYTE, data );
949 
950 	byte *data2 = (byte *)R_StaticAlloc( c * 4 );
951 
952 	for ( int i = 0 ; i < c ; i++ ) {
953 		data2[ i * 4 ] = data[ i * 3 ];
954 		data2[ i * 4 + 1 ] = data[ i * 3 + 1 ];
955 		data2[ i * 4 + 2 ] = data[ i * 3 + 2 ];
956 		data2[ i * 4 + 3 ] = 0xff;
957 	}
958 
959 	R_WriteTGA( fileName, data2, rc->width, rc->height, true );
960 
961 	R_StaticFree( data );
962 	R_StaticFree( data2 );
963 }
964 
965 
966 /*
967 ==============
968 AllocRenderWorld
969 ==============
970 */
AllocRenderWorld()971 idRenderWorld *idRenderSystemLocal::AllocRenderWorld() {
972 	idRenderWorldLocal *rw;
973 	rw = new idRenderWorldLocal;
974 	worlds.Append( rw );
975 	return rw;
976 }
977 
978 /*
979 ==============
980 FreeRenderWorld
981 ==============
982 */
FreeRenderWorld(idRenderWorld * rw)983 void idRenderSystemLocal::FreeRenderWorld( idRenderWorld *rw ) {
984 	if ( primaryWorld == rw ) {
985 		primaryWorld = NULL;
986 	}
987 	worlds.Remove( static_cast<idRenderWorldLocal *>(rw) );
988 	delete rw;
989 }
990 
991 /*
992 ==============
993 PrintMemInfo
994 ==============
995 */
PrintMemInfo(MemInfo_t * mi)996 void idRenderSystemLocal::PrintMemInfo( MemInfo_t *mi ) {
997 	// sum up image totals
998 	globalImages->PrintMemInfo( mi );
999 
1000 	// sum up model totals
1001 	renderModelManager->PrintMemInfo( mi );
1002 
1003 	// compute render totals
1004 
1005 }
1006 
1007 /*
1008 ===============
1009 idRenderSystemLocal::UploadImage
1010 ===============
1011 */
UploadImage(const char * imageName,const byte * data,int width,int height)1012 bool idRenderSystemLocal::UploadImage( const char *imageName, const byte *data, int width, int height  ) {
1013 	idImage *image = globalImages->GetImage( imageName );
1014 	if ( !image ) {
1015 		return false;
1016 	}
1017 	image->UploadScratch( data, width, height );
1018 	image->SetImageFilterAndRepeat();
1019 	return true;
1020 }
1021