1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //
21 // r_main.c
22 //
23
24 #include "rf_local.h"
25
26 refInfo_t ri;
27
28 /*
29 ==============================================================================
30
31 POLYGON BACKEND
32
33 Generic meshes can be passed from CGAME to be rendered here
34 ==============================================================================
35 */
36
37 static mesh_t r_polyMesh;
38
39 /*
40 ================
41 R_AddPolysToList
42 ================
43 */
R_AddPolysToList(void)44 static void R_AddPolysToList (void)
45 {
46 refPoly_t *p;
47 mQ3BspFog_t *fog;
48 uint32 i;
49
50 if (!r_drawPolys->intVal)
51 return;
52
53 // Add poly meshes to list
54 for (i=0 ; i<ri.scn.numPolys ; i++) {
55 p = ri.scn.polyList[i];
56
57 // Find fog
58 fog = R_FogForSphere (p->origin, p->radius);
59
60 // Add to the list
61 R_AddMeshToList (p->shader, p->shaderTime, NULL, fog, MBT_POLY, p);
62 }
63 }
64
65
66 /*
67 ================
68 R_PushPoly
69 ================
70 */
R_PushPoly(meshBuffer_t * mb,meshFeatures_t features)71 void R_PushPoly (meshBuffer_t *mb, meshFeatures_t features)
72 {
73 refPoly_t *p;
74
75 p = (refPoly_t *)mb->mesh;
76 if (p->numVerts > RB_MAX_VERTS)
77 return;
78
79 r_polyMesh.numIndexes = (p->numVerts - 2) * 3;
80 r_polyMesh.numVerts = p->numVerts;
81
82 r_polyMesh.colorArray = p->colors;
83 r_polyMesh.coordArray = p->texCoords;
84 r_polyMesh.vertexArray = p->vertices;
85
86 RB_PushMesh (&r_polyMesh, features);
87 }
88
89
90 /*
91 ================
92 R_PolyOverflow
93 ================
94 */
R_PolyOverflow(meshBuffer_t * mb)95 qBool R_PolyOverflow (meshBuffer_t *mb)
96 {
97 refPoly_t *p;
98
99 p = (refPoly_t *)mb->mesh;
100 return RB_BackendOverflow (p->numVerts, (p->numVerts - 2) * 3);
101 }
102
103
104 /*
105 ================
106 R_PolyInit
107 ================
108 */
R_PolyInit(void)109 void R_PolyInit (void)
110 {
111 r_polyMesh.indexArray = NULL;
112 r_polyMesh.lmCoordArray = NULL;
113 r_polyMesh.normalsArray = NULL;
114 r_polyMesh.sVectorsArray = NULL;
115 r_polyMesh.tVectorsArray = NULL;
116 r_polyMesh.trNeighborsArray = NULL;
117 r_polyMesh.trNormalsArray = NULL;
118 }
119
120 /*
121 =============================================================================
122
123 ENTITIES
124
125 =============================================================================
126 */
127
128 /*
129 =============
130 R_AddEntitiesToList
131 =============
132 */
R_AddEntitiesToList(void)133 static void R_AddEntitiesToList (void)
134 {
135 refEntity_t *ent;
136 uint32 i;
137
138 if (!r_drawEntities->intVal)
139 return;
140
141 // Add all entities to the list
142 for (i=ENTLIST_OFFSET, ent=&ri.scn.entityList[ENTLIST_OFFSET] ; i<ri.scn.numEntities ; ent++, i++) {
143 if (!ent->model) {
144 RB_AddNullModelToList (ent);
145 continue;
146 }
147
148 if (ri.scn.mirrorView) {
149 if (ent->flags & RF_WEAPONMODEL)
150 continue;
151 }
152
153 switch (ent->model->type) {
154 case MODEL_MD2:
155 case MODEL_MD3:
156 R_AddAliasModelToList (ent);
157 break;
158
159 case MODEL_Q2BSP:
160 R_AddQ2BrushModel (ent);
161 break;
162
163 case MODEL_Q3BSP:
164 R_AddQ3BrushModel (ent);
165 break;
166
167 case MODEL_SP2:
168 R_AddSP2ModelToList (ent);
169 break;
170
171 case MODEL_BAD:
172 default:
173 RB_AddNullModelToList (ent);
174 break;
175 }
176 }
177 }
178
179
180 /*
181 =============
182 R_EntityInit
183 =============
184 */
R_EntityInit(void)185 void R_EntityInit (void)
186 {
187 // Reserve a spot for the default entity
188 ri.scn.defaultEntity = &ri.scn.entityList[0];
189
190 memset (ri.scn.defaultEntity, 0, sizeof (refEntity_t));
191 ri.scn.defaultEntity->model = ri.scn.defaultModel;
192 ri.scn.defaultEntity->scale = 1.0f;
193 Matrix3_Identity (ri.scn.defaultEntity->axis);
194 Vec4Set (ri.scn.defaultEntity->color, 255, 255, 255, 255);
195
196 // And for the world entity
197 ri.scn.worldEntity = &ri.scn.entityList[1];
198 memcpy (ri.scn.worldEntity, ri.scn.defaultEntity, sizeof (refEntity_t));
199 }
200
201 /*
202 =============================================================================
203
204 SCENE
205
206 =============================================================================
207 */
208
209 /*
210 ==================
211 R_UpdateCvars
212
213 Updates scene based on cvar changes
214 ==================
215 */
R_UpdateCvars(void)216 static void R_UpdateCvars (void)
217 {
218 // Draw buffer stuff
219 if (gl_drawbuffer->modified) {
220 gl_drawbuffer->modified = qFalse;
221 if (!ri.cameraSeparation || !ri.config.stereoEnabled) {
222 if (!Q_stricmp (gl_drawbuffer->string, "GL_FRONT"))
223 qglDrawBuffer (GL_FRONT);
224 else
225 qglDrawBuffer (GL_BACK);
226 }
227 }
228
229 // Texturemode stuff
230 if (gl_texturemode->modified)
231 GL_TextureMode (qFalse, qFalse);
232
233 // Update anisotropy
234 if (r_ext_maxAnisotropy->modified)
235 GL_ResetAnisotropy ();
236
237 // Update font
238 if (r_defaultFont->modified)
239 R_CheckFont ();
240 if (r_fontScale->modified) {
241 r_fontScale->modified = qFalse;
242 if (r_fontScale->floatVal <= 0)
243 Cvar_VariableSetValue (r_fontScale, 1, qTrue);
244 }
245
246 // Gamma ramp
247 if (ri.config.hwGammaInUse && vid_gamma->modified)
248 R_UpdateGammaRamp ();
249
250 // Clamp zFar
251 if (r_zFarAbs->modified) {
252 r_zFarAbs->modified = qFalse;
253 if (r_zFarAbs->intVal < 0)
254 Cvar_VariableSetValue (r_zFarAbs, 0, qTrue);
255 }
256 if (r_zFarMin->modified) {
257 r_zFarMin->modified = qFalse;
258 if (r_zFarMin->intVal <= 0)
259 Cvar_VariableSetValue (r_zFarMin, 1, qTrue);
260 }
261
262 // Clamp zNear
263 if (r_zNear->modified) {
264 r_zNear->modified = qFalse;
265 if (r_zNear->floatVal < 0.1f)
266 Cvar_VariableSetValue (r_zNear, 4, qTrue);
267 }
268 }
269
270
271 /*
272 ================
273 R_RenderToList
274
275 Adds scene items to the desired list
276 ================
277 */
R_RenderToList(refDef_t * rd,meshList_t * list)278 void R_RenderToList (refDef_t *rd, meshList_t *list)
279 {
280 uint32 startTime;
281 int i;
282
283 if (r_times->intVal)
284 startTime = Sys_UMilliseconds ();
285
286 ri.def = *rd;
287 r_currentList = list;
288
289 for (i=0 ; i<MAX_MESH_KEYS ; i++)
290 r_currentList->numMeshes[i] = 0;
291 for (i=0 ; i<MAX_ADDITIVE_KEYS ; i++)
292 r_currentList->numAdditiveMeshes[i] = 0;
293 r_currentList->skyDrawn = qFalse;
294
295 RB_SetupGL3D ();
296 R_SetupFrustum ();
297
298 R_AddSkyToList ();
299 R_AddWorldToList ();
300 R_AddDecalsToList ();
301 R_AddPolysToList ();
302 R_AddEntitiesToList ();
303 R_SortMeshList ();
304 R_DrawMeshList (qFalse);
305 R_DrawMeshOutlines ();
306 RB_DrawNullModelList ();
307 RB_DrawDLights ();
308
309 if (ri.scn.mirrorView || ri.scn.portalView)
310 qglDisable (GL_CLIP_PLANE0);
311
312 if (r_times->intVal)
313 ri.pc.timeAddToList += Sys_UMilliseconds () - startTime;
314 }
315
316
317 /*
318 ================
319 R_RenderScene
320 ================
321 */
R_RenderScene(refDef_t * rd)322 void R_RenderScene (refDef_t *rd)
323 {
324 if (r_noRefresh->intVal)
325 return;
326
327 if (!ri.scn.worldModel->touchFrame && !(rd->rdFlags & RDF_NOWORLDMODEL))
328 Com_Error (ERR_DROP, "R_RenderScene: NULL worldmodel");
329
330 ri.scn.zFar = 0;
331 ri.scn.mirrorView = qFalse;
332 ri.scn.portalView = qFalse;
333
334 if (gl_finish->intVal)
335 qglFinish ();
336
337 R_RenderToList (rd, &r_worldList);
338 R_SetLightLevel ();
339
340 #ifdef SHADOW_VOLUMES
341 RB_ShadowBlend ();
342 #endif
343
344 RB_SetupGL2D ();
345 }
346
347
348 /*
349 ==================
350 R_BeginFrame
351 ==================
352 */
R_BeginFrame(float cameraSeparation)353 void R_BeginFrame (float cameraSeparation)
354 {
355 ri.cameraSeparation = cameraSeparation;
356
357 // Frame logging
358 if (gl_log->modified) {
359 gl_log->modified = qFalse;
360 QGL_ToggleLogging ();
361 }
362 QGL_LogBeginFrame ();
363
364 // Debugging
365 if (qgl_debug->modified) {
366 qgl_debug->modified = qFalse;
367 QGL_ToggleDebug ();
368 }
369
370 // Setup the frame for rendering
371 GLimp_BeginFrame ();
372
373 // Go into 2D mode
374 RB_SetupGL2D ();
375
376 // Apply cvar settings
377 R_UpdateCvars ();
378
379 // Clear the scene if desired
380 RB_ClearBuffers ();
381
382 // Update the backend
383 RB_BeginFrame ();
384 }
385
386
387 /*
388 ==================
389 R_EndFrame
390 ==================
391 */
R_EndFrame(void)392 void R_EndFrame (void)
393 {
394 // Update the backend
395 RB_EndFrame ();
396
397 // Swap buffers
398 GLimp_EndFrame ();
399
400 // Go into 2D mode
401 RB_SetupGL2D ();
402
403 // Frame logging
404 QGL_LogEndFrame ();
405
406 // Rendering speeds
407 if (r_speeds->intVal || r_times->intVal || r_debugBatching->intVal || r_debugCulling->intVal) {
408 // FIXME: Check ->modified and set to false, if it's modified to turn on then clear ri.pc and skip the first time for accuracy
409 // General rendering information
410 if (r_speeds->intVal) {
411 Com_Printf (0, "\n");
412 Com_Printf (0, "%3u ent %3u aelem %4u apoly %4u poly %3u dlight\n",
413 ri.scn.numEntities-ENTLIST_OFFSET, ri.pc.aliasElements, ri.pc.aliasPolys,
414 ri.scn.numPolys, ri.scn.numDLights);
415
416 Com_Printf (0, "%.2f mtexel %3u unit %3u envchg %4u binds (%4u unique)\n",
417 ri.pc.texelsInUse/1000000.0f, ri.pc.textureUnitChanges,
418 ri.pc.textureEnvChanges, ri.pc.textureBinds, ri.pc.texturesInUse);
419
420 if (ri.scn.worldModel->touchFrame && !(ri.def.rdFlags & RDF_NOWORLDMODEL)) {
421 Com_Printf (0, "%4u wpoly %4u welem %4u decal %6.f zfar\n",
422 ri.pc.worldPolys, ri.pc.worldElements, ri.scn.drawnDecals, ri.scn.zFar);
423 }
424
425 Com_Printf (0, "%5u vert %5u tris %4u elem %4u mesh %4u pass %3u gls\n",
426 ri.pc.numVerts, ri.pc.numTris, ri.pc.numElements,
427 ri.pc.meshCount, ri.pc.meshPasses,
428 ri.pc.stateChanges);
429 }
430
431 // Time to process things
432 if (r_times->intVal) {
433 Com_Printf (0, "\n");
434 Com_Printf (0, "%3u add %3u sort %3u draw\n",
435 ri.pc.timeAddToList, ri.pc.timeSortList, ri.pc.timeDrawList);
436
437 if (ri.scn.worldModel->touchFrame && !(ri.def.rdFlags & RDF_NOWORLDMODEL)) {
438 Com_Printf (0, "%3u marklv %3u marklt %3u recurs\n",
439 ri.pc.timeMarkLeaves, ri.pc.timeMarkLights, ri.pc.timeRecurseWorld);
440 }
441 }
442
443 // Batch information
444 if (r_debugBatching->intVal) {
445 Com_Printf (0, "\n");
446 Com_Printf (0, "%4i batch %4i flush\n",
447 ri.pc.meshBatches, ri.pc.meshBatchFlush);
448
449 if (ri.pc.meshBatches && ri.pc.meshBatchFlush)
450 Com_Printf (0, "%5.2f efficiency\n",
451 100.0f - ((float)ri.pc.meshBatchFlush/(float)ri.pc.meshBatches));
452 }
453
454 // Cull information
455 if (r_debugCulling->intVal && !r_noCull->intVal) {
456 Com_Printf (0, "\n");
457 Com_Printf (0, "bounds[%3i/%3i] planar[%3i/%3i] radii[%3i/%3i] visFrame[%3i/%3i] surfFrame[%3i/%3i]\n",
458 ri.pc.cullBounds[CULL_PASS], ri.pc.cullBounds[CULL_FAIL],
459 ri.pc.cullPlanar[CULL_PASS], ri.pc.cullPlanar[CULL_FAIL],
460 ri.pc.cullRadius[CULL_PASS], ri.pc.cullRadius[CULL_FAIL],
461 ri.pc.cullVis[CULL_PASS], ri.pc.cullVis[CULL_FAIL],
462 ri.pc.cullSurf[CULL_PASS], ri.pc.cullSurf[CULL_FAIL]);
463 }
464
465 memset (&ri.pc, 0, sizeof (refStats_t));
466 }
467
468 // Next frame
469 ri.frameCount++;
470 }
471
472 // ==========================================================
473
474 /*
475 ====================
476 R_ClearScene
477 ====================
478 */
R_ClearScene(void)479 void R_ClearScene (void)
480 {
481 ri.scn.numDecals = 0;
482 ri.scn.drawnDecals = 0;
483
484 ri.scn.numDLights = 0;
485 ri.scn.numEntities = ENTLIST_OFFSET;
486 ri.scn.numPolys = 0;
487 }
488
489
490 /*
491 =====================
492 R_AddDecal
493 =====================
494 */
R_AddDecal(refDecal_t * decal,bvec4_t color,struct shader_s * material,float materialTime)495 void R_AddDecal (refDecal_t *decal, bvec4_t color, struct shader_s *material, float materialTime)
496 {
497 int i;
498
499 if (!decal || ri.scn.numDecals >= MAX_REF_DECALS)
500 return;
501
502 // Adjust color
503 if (color) {
504 for (i=0 ; i<decal->poly.numVerts ; i++)
505 *(int *)decal->poly.colors[i] = *(int *)color;
506 }
507
508 // Material
509 decal->poly.shader = material;
510 if (!decal->poly.shader)
511 decal->poly.shader = r_noShader;
512 decal->poly.shaderTime = materialTime;
513
514 // FIXME: adjust bmodel decals here
515
516 // Store
517 ri.scn.decalList[ri.scn.numDecals++] = decal;
518 }
519
520
521 /*
522 =====================
523 R_AddEntity
524 =====================
525 */
R_AddEntity(refEntity_t * ent)526 void R_AddEntity (refEntity_t *ent)
527 {
528 if (ri.scn.numEntities >= MAX_REF_ENTITIES)
529 return;
530 if (ent->color[3] <= 0)
531 return;
532
533 ri.scn.entityList[ri.scn.numEntities] = *ent;
534 if (ent->color[3] < 255)
535 ri.scn.entityList[ri.scn.numEntities].flags |= RF_TRANSLUCENT;
536
537 ri.scn.numEntities++;
538 }
539
540
541 /*
542 =====================
543 R_AddPoly
544 =====================
545 */
R_AddPoly(refPoly_t * poly)546 void R_AddPoly (refPoly_t *poly)
547 {
548 if (ri.scn.numPolys >= MAX_REF_POLYS)
549 return;
550
551 // Material
552 if (!poly->shader)
553 poly->shader = r_noShader;
554
555 // Store
556 ri.scn.polyList[ri.scn.numPolys++] = poly;
557 }
558
559
560 /*
561 =====================
562 R_AddLight
563 =====================
564 */
R_AddLight(vec3_t origin,float intensity,float r,float g,float b)565 void R_AddLight (vec3_t origin, float intensity, float r, float g, float b)
566 {
567 refDLight_t *dl;
568
569 if (ri.scn.numDLights >= MAX_REF_DLIGHTS)
570 return;
571
572 if (!intensity)
573 return;
574
575 dl = &ri.scn.dLightList[ri.scn.numDLights++];
576
577 Vec3Copy (origin, dl->origin);
578 Vec3Set (dl->color, r, g, b);
579 dl->intensity = intensity;
580
581 R_LightBounds (origin, intensity, dl->mins, dl->maxs);
582 }
583
584
585 /*
586 =====================
587 R_AddLightStyle
588 =====================
589 */
R_AddLightStyle(int style,float r,float g,float b)590 void R_AddLightStyle (int style, float r, float g, float b)
591 {
592 refLightStyle_t *ls;
593
594 if (style < 0 || style > MAX_CS_LIGHTSTYLES) {
595 Com_Error (ERR_DROP, "Bad light style %i", style);
596 return;
597 }
598
599 ls = &ri.scn.lightStyles[style];
600
601 ls->white = r+g+b;
602 Vec3Set (ls->rgb, r, g, b);
603 }
604
605 /*
606 =============================================================================
607
608 MISC
609
610 =============================================================================
611 */
612
613 /*
614 ==================
615 GL_CheckForError
616 ==================
617 */
GetGLErrorString(GLenum error)618 static inline const char *GetGLErrorString (GLenum error)
619 {
620 switch (error) {
621 case GL_INVALID_ENUM: return "INVALID ENUM";
622 case GL_INVALID_OPERATION: return "INVALID OPERATION";
623 case GL_INVALID_VALUE: return "INVALID VALUE";
624 case GL_NO_ERROR: return "NO ERROR";
625 case GL_OUT_OF_MEMORY: return "OUT OF MEMORY";
626 case GL_STACK_OVERFLOW: return "STACK OVERFLOW";
627 case GL_STACK_UNDERFLOW: return "STACK UNDERFLOW";
628 }
629
630 return "unknown";
631 }
GL_CheckForError(char * where)632 void GL_CheckForError (char *where)
633 {
634 GLenum error;
635
636 error = qglGetError ();
637 if (error != GL_NO_ERROR) {
638 Com_Printf (PRNT_ERROR, "GL_ERROR: '%s' (0x%x)", GetGLErrorString (error), error);
639 if (where)
640 Com_Printf (0, " %s\n", where);
641 else
642 Com_Printf (0, "\n");
643 }
644 }
645
646
647 /*
648 =============
649 R_GetRefConfig
650 =============
651 */
R_GetRefConfig(refConfig_t * outConfig)652 void R_GetRefConfig (refConfig_t *outConfig)
653 {
654 *outConfig = ri.config;
655 }
656
657
658 /*
659 =============
660 R_TransformToScreen_Vec3
661 =============
662 */
R_TransformToScreen_Vec3(vec3_t in,vec3_t out)663 void R_TransformToScreen_Vec3 (vec3_t in, vec3_t out)
664 {
665 vec4_t temp, temp2;
666
667 temp[0] = in[0];
668 temp[1] = in[1];
669 temp[2] = in[2];
670 temp[3] = 1.0f;
671 Matrix4_Multiply_Vector (ri.scn.worldViewMatrix, temp, temp2);
672 Matrix4_Multiply_Vector (ri.scn.projectionMatrix, temp2, temp);
673
674 if (!temp[3])
675 return;
676 out[0] = ri.def.x + (temp[0] / temp[3] + 1.0f) * ri.def.width * 0.5f;
677 out[1] = ri.def.y + (temp[1] / temp[3] + 1.0f) * ri.def.height * 0.5f;
678 out[2] = (temp[2] / temp[3] + 1.0f) * 0.5f;
679 }
680
681
682 /*
683 =============
684 R_TransformVectorToScreen
685 =============
686 */
R_TransformVectorToScreen(refDef_t * rd,vec3_t in,vec2_t out)687 void R_TransformVectorToScreen (refDef_t *rd, vec3_t in, vec2_t out)
688 {
689 mat4x4_t p, m;
690 vec4_t temp, temp2;
691
692 if (!rd || !in || !out)
693 return;
694
695 temp[0] = in[0];
696 temp[1] = in[1];
697 temp[2] = in[2];
698 temp[3] = 1.0f;
699
700 R_SetupProjectionMatrix (rd, p);
701 R_SetupModelviewMatrix (rd, m);
702
703 Matrix4_Multiply_Vector (m, temp, temp2);
704 Matrix4_Multiply_Vector (p, temp2, temp);
705
706 if (!temp[3])
707 return;
708 out[0] = rd->x + (temp[0] / temp[3] + 1.0f) * rd->width * 0.5f;
709 out[1] = rd->y + (temp[1] / temp[3] + 1.0f) * rd->height * 0.5f;
710 }
711
712 /*
713 =============================================================================
714
715 REGISTRATION
716
717 =============================================================================
718 */
719
720 /*
721 ==================
722 R_BeginRegistration
723
724 Starts refresh registration before map load
725 ==================
726 */
R_BeginRegistration(void)727 void R_BeginRegistration (void)
728 {
729 // Clear the scene so that old scene object pointers are cleared
730 R_ClearScene ();
731
732 // Clear old registration values
733 ri.reg.fontsReleased = 0;
734 ri.reg.fontsSeaked = 0;
735 ri.reg.fontsTouched = 0;
736 ri.reg.imagesReleased = 0;
737 ri.reg.imagesResampled = 0;
738 ri.reg.imagesSeaked = 0;
739 ri.reg.imagesTouched = 0;
740 ri.reg.modelsReleased = 0;
741 ri.reg.modelsSeaked = 0;
742 ri.reg.modelsTouched = 0;
743 ri.reg.shadersReleased = 0;
744 ri.reg.shadersSeaked = 0;
745 ri.reg.shadersTouched = 0;
746
747 // Begin sub-system registration
748 ri.reg.inSequence = qTrue;
749 ri.reg.registerFrame++;
750
751 R_BeginImageRegistration ();
752 }
753
754
755 /*
756 ==================
757 R_EndRegistration
758
759 Called at the end of all registration by the client
760 ==================
761 */
R_EndRegistration(void)762 void R_EndRegistration (void)
763 {
764 R_EndFontRegistration (); // Register first so shaders are touched
765 R_EndModelRegistration (); // Register first so shaders are touched
766 R_EndShaderRegistration (); // Register first so programs and images are touched
767 R_EndImageRegistration ();
768
769 ri.reg.inSequence = qFalse;
770
771 // Print registration info
772 Com_Printf (PRNT_CONSOLE, "Registration sequence completed...\n");
773 Com_Printf (PRNT_CONSOLE, "Fonts rel/touch/seak: %i/%i/%i\n", ri.reg.fontsReleased, ri.reg.fontsTouched, ri.reg.fontsSeaked);
774 Com_Printf (PRNT_CONSOLE, "Models rel/touch/seak: %i/%i/%i\n", ri.reg.modelsReleased, ri.reg.modelsTouched, ri.reg.modelsSeaked);
775 Com_Printf (PRNT_CONSOLE, "Shaders rel/touch/seak: %i/%i/%i\n", ri.reg.shadersReleased, ri.reg.shadersTouched, ri.reg.shadersSeaked);
776 Com_Printf (PRNT_CONSOLE, "Images rel/resamp/seak/touch: %i/%i/%i/%i\n", ri.reg.imagesReleased, ri.reg.imagesResampled, ri.reg.imagesSeaked, ri.reg.imagesTouched);
777 }
778