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