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