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