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 // cg_view.c
22 //
23 
24 #include "cg_local.h"
25 
26 /*
27 =======================================================================
28 
29 	SCREEN SIZE
30 
31 =======================================================================
32 */
33 
34 /*
35 ==============
36 V_TileClear
37 ==============
38 */
V_TileRect(int x,int y,int w,int h)39 static void V_TileRect (int x, int y, int w, int h)
40 {
41 	if (w == 0 || h == 0)
42 		return;	// Prevents div by zero (should never happen)
43 
44 	if (cgMedia.tileBackShader)
45 		cgi.R_DrawPic (cgMedia.tileBackShader, 0,
46 		(float)x, (float)y, (float)w, (float)h,
47 		x/64.0f, y/64.0f, (x+w)/64.0f, (y+h)/64.0f, Q_colorWhite);
48 	else
49 		CG_DrawFill ((float)x, (float)y, (float)w, (float)h, Q_colorBlack);
50 }
V_TileClear(void)51 static void V_TileClear (void)
52 {
53 	int		top, bottom, left, right;
54 
55 	if (viewsize->intVal >= 100)
56 		return;		// Full screen rendering
57 
58 	top = cg.refDef.y;
59 	bottom = top + cg.refDef.height-1;
60 	left = cg.refDef.x;
61 	right = left + cg.refDef.width-1;
62 
63 	// Clear above view screen
64 	V_TileRect (0, 0, cg.refConfig.vidWidth, top);
65 
66 	// Clear below view screen
67 	V_TileRect (0, bottom, cg.refConfig.vidWidth, cg.refConfig.vidHeight - bottom);
68 
69 	// Clear left of view screen
70 	V_TileRect (0, top, left, bottom - top + 1);
71 
72 	// Clear right of view screen
73 	V_TileRect (right, top, cg.refConfig.vidWidth - right, bottom - top + 1);
74 }
75 
76 
77 /*
78 =================
79 V_CalcVrect
80 
81 Sets the coordinates of the rendered window
82 =================
83 */
V_CalcVrect(void)84 static void V_CalcVrect (void)
85 {
86 	int		size;
87 
88 	// Bound viewsize
89 	if (viewsize->intVal < 40)
90 		cgi.Cvar_SetValue ("viewsize", 40, qTrue);
91 	if (viewsize->intVal > 100)
92 		cgi.Cvar_SetValue ("viewsize", 100, qTrue);
93 
94 	size = viewsize->intVal;
95 
96 	cg.refDef.width = cg.refConfig.vidWidth*size/100;
97 	cg.refDef.width &= ~7;
98 
99 	cg.refDef.height = cg.refConfig.vidHeight*size/100;
100 	cg.refDef.height &= ~1;
101 
102 	cg.refDef.x = (cg.refConfig.vidWidth - cg.refDef.width)/2;
103 	cg.refDef.y = (cg.refConfig.vidHeight - cg.refDef.height)/2;
104 
105 	// Clear the background
106 	V_TileClear ();
107 }
108 
109 
110 /*
111 =================
112 V_SizeUp_f
113 =================
114 */
V_SizeUp_f(void)115 static void V_SizeUp_f (void)
116 {
117 	cgi.Cvar_SetValue ("viewsize", viewsize->intVal + 10.0f, qTrue);
118 }
119 
120 
121 /*
122 =================
123 V_SizeDown_f
124 =================
125 */
V_SizeDown_f(void)126 static void V_SizeDown_f (void)
127 {
128 	cgi.Cvar_SetValue ("viewsize", viewsize->intVal - 10.0f, qTrue);
129 }
130 
131 /*
132 =======================================================================
133 
134 	VIEW RENDERING
135 
136 =======================================================================
137 */
138 
139 /*
140 ================
141 V_TestParticles
142 ================
143 */
144 static cgParticle_t v_testParticleList[PT_PICTOTAL];
V_TestParticles(void)145 static void V_TestParticles (void)
146 {
147 	int				i;
148 	float			d, r, u;
149 	vec3_t			origin;
150 	float			scale;
151 	bvec4_t			outColor;
152 	cgParticle_t	*p;
153 
154 	cgi.R_ClearScene ();
155 	Vec4Set (outColor, 255, 255, 255, 255);
156 	scale = 1;
157 
158 	for (p=&v_testParticleList[0], i=0 ; i<PT_PICTOTAL ; i++, p++) {
159 		d = i*0.5f;
160 		r = 3*((i&7)-3.5f);
161 		u = 3*(((i>>3)&7)-3.5f);
162 
163 		// Center
164 		origin[0] = cg.refDef.viewOrigin[0] + cg.refDef.viewAxis[0][0]*d - cg.refDef.viewAxis[1][0]*r + cg.refDef.viewAxis[2][0]*u;
165 		origin[1] = cg.refDef.viewOrigin[1] + cg.refDef.viewAxis[0][1]*d - cg.refDef.viewAxis[1][1]*r + cg.refDef.viewAxis[2][1]*u;
166 		origin[2] = cg.refDef.viewOrigin[2] + cg.refDef.viewAxis[0][2]*d - cg.refDef.viewAxis[1][2]*r + cg.refDef.viewAxis[2][2]*u;
167 
168 		// Top left
169 		*(int *)v_testParticleList[i].outColor[0] = *(int *)outColor;
170 		Vec2Set (v_testParticleList[i].outCoords[0], 0, 0);
171 		Vec3Set (v_testParticleList[i].outVertices[0],	origin[0] + cg.refDef.viewAxis[2][0]*scale + cg.refDef.viewAxis[1][0]*scale,
172 														origin[1] + cg.refDef.viewAxis[2][1]*scale + cg.refDef.viewAxis[1][1]*scale,
173 														origin[2] + cg.refDef.viewAxis[2][2]*scale + cg.refDef.viewAxis[1][2]*scale);
174 
175 		// Bottom left
176 		*(int *)v_testParticleList[i].outColor[0] = *(int *)outColor;
177 		Vec2Set (v_testParticleList[i].outCoords[1], 0, 1);
178 		Vec3Set (v_testParticleList[i].outVertices[1],	origin[0] - cg.refDef.viewAxis[2][0]*scale + cg.refDef.viewAxis[1][0]*scale,
179 														origin[1] - cg.refDef.viewAxis[2][1]*scale + cg.refDef.viewAxis[1][1]*scale,
180 														origin[2] - cg.refDef.viewAxis[2][2]*scale + cg.refDef.viewAxis[1][2]*scale);
181 
182 		// Bottom right
183 		*(int *)v_testParticleList[i].outColor[0] = *(int *)outColor;
184 		Vec2Set (v_testParticleList[i].outCoords[2], 1, 1);
185 		Vec3Set (v_testParticleList[i].outVertices[2],	origin[0] - cg.refDef.viewAxis[2][0]*scale - cg.refDef.viewAxis[1][0]*scale,
186 														origin[1] - cg.refDef.viewAxis[2][1]*scale - cg.refDef.viewAxis[1][1]*scale,
187 														origin[2] - cg.refDef.viewAxis[2][2]*scale - cg.refDef.viewAxis[1][2]*scale);
188 
189 		// Top right
190 		*(int *)v_testParticleList[i].outColor[0] = *(int *)outColor;
191 		Vec2Set (v_testParticleList[i].outCoords[3], 1, 0);
192 		Vec3Set (v_testParticleList[i].outVertices[3],	origin[0] + cg.refDef.viewAxis[2][0]*scale - cg.refDef.viewAxis[1][0]*scale,
193 														origin[1] + cg.refDef.viewAxis[2][1]*scale - cg.refDef.viewAxis[1][1]*scale,
194 														origin[2] + cg.refDef.viewAxis[2][2]*scale - cg.refDef.viewAxis[1][2]*scale);
195 
196 		// Render it
197 		p->outPoly.numVerts = 4;
198 		p->outPoly.colors = v_testParticleList[i].outColor;
199 		p->outPoly.texCoords = v_testParticleList[i].outCoords;
200 		p->outPoly.vertices = v_testParticleList[i].outVertices;
201 		p->outPoly.shader = cgMedia.particleTable[i % PT_PICTOTAL];
202 		p->outPoly.shaderTime = 0;
203 
204 		cgi.R_AddPoly (&p->outPoly);
205 	}
206 }
207 
208 
209 /*
210 ================
211 V_TestEntities
212 ================
213 */
V_TestEntities(void)214 static void V_TestEntities (void)
215 {
216 	int			i, j;
217 	float		f, r;
218 	refEntity_t	ent;
219 
220 	cgi.R_ClearScene ();
221 	for (i=0 ; i<32 ; i++) {
222 		r = 64 * ((i%4) - 1.5f);
223 		f = 64 * (i/40.f) + 128;
224 
225 		for (j=0 ; j<3 ; j++) {
226 			ent.origin[j] = cg.refDef.viewOrigin[j] + cg.refDef.viewAxis[0][j]*f - cg.refDef.viewAxis[1][j]*r;
227 			ent.oldOrigin[j] = ent.origin[j];
228 		}
229 
230 		Matrix3_Identity (ent.axis);
231 
232 		ent.model = cg.baseClientInfo.model;
233 		ent.skin = cg.baseClientInfo.skin;
234 		ent.skinNum = 0;
235 
236 		ent.flags = 0;
237 		ent.scale = 1;
238 		Vec4Set (ent.color, 255, 255, 255, 255);
239 
240 		ent.backLerp = 0;
241 		ent.frame = 0;
242 		ent.oldFrame = 0;
243 
244 		cgi.R_AddEntity (&ent);
245 	}
246 }
247 
248 
249 /*
250 ================
251 V_TestLights
252 ================
253 */
V_TestLights(void)254 static void V_TestLights (void)
255 {
256 	int			i;
257 	float		f, r;
258 	vec3_t		origin;
259 	float		red, green, blue;
260 
261 	for (i=0 ; i<32 ; i++) {
262 		r = 64 * ((i%4) - 1.5f);
263 		f = 64 * (i/40.f) + 128;
264 
265 		origin[0] = cg.refDef.viewOrigin[0] + cg.refDef.viewAxis[0][0]*f - cg.refDef.viewAxis[1][0]*r;
266 		origin[1] = cg.refDef.viewOrigin[1] + cg.refDef.viewAxis[0][1]*f - cg.refDef.viewAxis[1][1]*r;
267 		origin[2] = cg.refDef.viewOrigin[2] + cg.refDef.viewAxis[0][2]*f - cg.refDef.viewAxis[1][2]*r;
268 
269 		red = (float)(((i%6)+1) & 1);
270 		green = (float)((((i%6)+1) & 2)>>1);
271 		blue = (float)((((i%6)+1) & 4)>>2);
272 
273 		cgi.R_AddLight (origin, 200, red, green, blue);
274 	}
275 }
276 
277 // ====================================================================
278 
279 /*
280 ===============
281 V_CalcThirdPersonView
282 ===============
283 */
ClipCam(vec3_t start,vec3_t end,vec3_t newPos)284 static void ClipCam (vec3_t start, vec3_t end, vec3_t newPos)
285 {
286 	trace_t	tr;
287 	vec3_t	mins, maxs;
288 
289 	Vec3Set (mins, -5, -5, -5);
290 	Vec3Set (maxs, 5, 5, 5);
291 
292 	CG_PMTrace (&tr, start, mins, maxs, end, qTrue);
293 
294 	newPos[0] = tr.endPos[0];
295 	newPos[1] = tr.endPos[1];
296 	newPos[2] = tr.endPos[2];
297 }
V_CalcThirdPersonView(void)298 static void V_CalcThirdPersonView (void)
299 {
300 	vec3_t	end, camPosition;
301 	vec3_t	dir, newAngles;
302 	float	upDist, backDist, angle;
303 
304 	// Set the camera angle
305 	if (cg_thirdPersonAngle->modified) {
306 		cg_thirdPersonAngle->modified = qFalse;
307 
308 		if (cg_thirdPersonAngle->floatVal < 0.0f)
309 			cgi.Cvar_SetValue ("cg_thirdPersonAngle", 0.0f, qTrue);
310 	}
311 
312 	// Set the camera distance
313 	if (cg_thirdPersonDist->modified) {
314 		cg_thirdPersonDist->modified = qFalse;
315 
316 		if (cg_thirdPersonDist->floatVal < 1.0f)
317 			cgi.Cvar_SetValue ("cg_thirdPersonDist", 1.0f, qTrue);
318 	}
319 
320 	// Trig stuff
321 	angle = M_PI * (cg_thirdPersonAngle->floatVal / 180.0f);
322 	upDist = cg_thirdPersonDist->floatVal * sin (angle);
323 	backDist = cg_thirdPersonDist->floatVal * cos (angle);
324 
325 	// Move up
326 	Vec3MA (cg.refDef.viewOrigin, -backDist, cg.refDef.viewAxis[0], end);
327 	Vec3MA (end, upDist, cg.refDef.viewAxis[2], end);
328 
329 	// Clip
330 	ClipCam (cg.refDef.viewOrigin, end, camPosition);
331 
332 	// Adjust player transparency
333 	cg.cameraTrans = Vec3DistFast (cg.refDef.viewOrigin, camPosition);
334 	if (cg.cameraTrans < cg_thirdPersonDist->floatVal) {
335 		cg.cameraTrans = (cg.cameraTrans / cg_thirdPersonDist->floatVal) * 255;
336 
337 		if (cg.cameraTrans == 0)
338 			return;
339 		else if (cg.cameraTrans > 245)
340 			cg.cameraTrans = 245;
341 	}
342 	else {
343 		cg.cameraTrans = 255;
344 	}
345 
346 	// Clip and adjust aim
347 	if (cg_thirdPersonClip->intVal) {
348 		Vec3MA (cg.refDef.viewOrigin, 8192, cg.refDef.viewAxis[0], dir);
349 		ClipCam (cg.refDef.viewOrigin, dir, newAngles);
350 
351 		Vec3Subtract (newAngles, camPosition, dir);
352 		VectorNormalizef (dir, dir);
353 		VecToAngles (dir, newAngles);
354 
355 		// Apply
356 		Vec3Copy (newAngles, cg.refDef.viewAngles);
357 		Angles_Matrix3 (cg.refDef.viewAngles, cg.refDef.viewAxis);
358 		Vec3Negate (cg.refDef.viewAxis[1], cg.refDef.rightVec);
359 	}
360 
361 	Vec3Copy (camPosition, cg.refDef.viewOrigin);
362 }
363 
364 
365 /*
366 ===============
367 V_CalcViewValues
368 
369 Sets cg.refDef view values
370 ===============
371 */
V_CalcViewValues(void)372 static void V_CalcViewValues (void)
373 {
374 	playerStateNew_t	*ps, *ops;
375 	cgEntity_t			*ent;
376 	int					i;
377 
378 	// Set cg.lerpFrac
379 	if (cgi.Cvar_GetIntegerValue ("timedemo"))
380 		cg.lerpFrac = 1.0f;
381 	else
382 		cg.lerpFrac = 1.0f - (cg.frame.serverTime - cg.refreshTime)*0.01f;
383 
384 	// Find the previous frame to interpolate from
385 	ps = &cg.frame.playerState;
386 	if (cg.oldFrame.serverFrame != cg.frame.serverFrame-1 || !cg.oldFrame.valid)
387 		ops = &cg.frame.playerState;	// previous frame was dropped or invalid
388 	else
389 		ops = &cg.oldFrame.playerState;
390 
391 	// See if the player entity was teleported this frame
392 	if (fabs (ops->pMove.origin[0] - ps->pMove.origin[0]) > 256*8
393 	|| abs (ops->pMove.origin[1] - ps->pMove.origin[1]) > 256*8
394 	|| abs (ops->pMove.origin[2] - ps->pMove.origin[2]) > 256*8)
395 		ops = ps;		// don't interpolate
396 
397 	ent = &cg_entityList[cg.playerNum+1];
398 
399 	// Calculate the origin
400 	if (cl_predict->intVal && !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION)) {
401 		// Use predicted values
402 		uint32		delta;
403 		float		backLerp;
404 
405 		backLerp = 1.0f - cg.lerpFrac;
406 		for (i=0 ; i<3 ; i++) {
407 			cg.refDef.viewOrigin[i] = cg.predicted.origin[i] + ops->viewOffset[i]
408 								+ cg.lerpFrac*(ps->viewOffset[i] - ops->viewOffset[i])
409 								- backLerp*cg.predicted.error[i];
410 		}
411 
412 		// Smooth out stair climbing
413 		delta = cg.realTime - cg.predicted.stepTime;
414 		if (delta < 150)
415 			cg.refDef.viewOrigin[2] -= cg.predicted.step * (150 - delta) / 150;
416 	}
417 	else {
418 		// Just use interpolated values
419 		for (i=0 ; i<3 ; i++) {
420 			cg.refDef.viewOrigin[i] = ops->pMove.origin[i]*(1.0f/8.0f) + ops->viewOffset[i]
421 								+ cg.lerpFrac*(ps->pMove.origin[i]*(1.0f/8.0f) + ps->viewOffset[i]
422 								- (ops->pMove.origin[i]*(1.0f/8.0f) + ops->viewOffset[i]));
423 		}
424 	}
425 
426 	// If not running a demo or on a locked frame, add the local angle movement
427 	if (cg.frame.playerState.pMove.pmType < PMT_DEAD && !cg.attractLoop) {
428 		// Use predicted values
429 		Vec3Copy (cg.predicted.angles, cg.refDef.viewAngles);
430 	}
431 	else {
432 		// Just use interpolated values
433 		if (cg.frame.playerState.pMove.pmType >= PMT_DEAD && ops->pMove.pmType < PMT_DEAD) {
434 			cg.refDef.viewAngles[0] = LerpAngle (cg.predicted.angles[0], ps->viewAngles[0], cg.lerpFrac);
435 			cg.refDef.viewAngles[1] = LerpAngle (cg.predicted.angles[1], ps->viewAngles[1], cg.lerpFrac);
436 			cg.refDef.viewAngles[2] = LerpAngle (cg.predicted.angles[2], ps->viewAngles[2], cg.lerpFrac);
437 		}
438 		else {
439 			cg.refDef.viewAngles[0] = LerpAngle (ops->viewAngles[0], ps->viewAngles[0], cg.lerpFrac);
440 			cg.refDef.viewAngles[1] = LerpAngle (ops->viewAngles[1], ps->viewAngles[1], cg.lerpFrac);
441 			cg.refDef.viewAngles[2] = LerpAngle (ops->viewAngles[2], ps->viewAngles[2], cg.lerpFrac);
442 		}
443 	}
444 
445 	// Add kick angles
446 	cg.refDef.viewAngles[0] += LerpAngle (ops->kickAngles[0], ps->kickAngles[0], cg.lerpFrac);
447 	cg.refDef.viewAngles[1] += LerpAngle (ops->kickAngles[1], ps->kickAngles[1], cg.lerpFrac);
448 	cg.refDef.viewAngles[2] += LerpAngle (ops->kickAngles[2], ps->kickAngles[2], cg.lerpFrac);
449 
450 	// Calculate direction vectors
451 	Angles_Matrix3 (cg.refDef.viewAngles, cg.refDef.viewAxis);
452 	Vec3Negate (cg.refDef.viewAxis[1], cg.refDef.rightVec);
453 
454 	// Interpolate field of view
455 	cg.refDef.fovX = ops->fov + cg.lerpFrac * (ps->fov - ops->fov);
456 
457 	// Don't interpolate blend color
458 	Vec4Copy (ps->viewBlend, cg.viewBlend);
459 
460 	// Offset if in third person
461 	if (cg.thirdPerson)
462 		V_CalcThirdPersonView ();
463 }
464 
465 
466 /*
467 ==================
468 V_RenderView
469 ==================
470 */
471 #define FRAMETIME_MAX 0.5
V_RenderView(int realTime,float netFrameTime,float refreshFrameTime,float stereoSeparation,qBool refreshPrepped)472 void V_RenderView (int realTime, float netFrameTime, float refreshFrameTime, float stereoSeparation, qBool refreshPrepped)
473 {
474 	// Check cvar sanity
475 	CG_UpdateCvars ();
476 
477 	// Calculate screen dimensions and clear the background
478 	V_CalcVrect ();
479 
480 	// Set time
481 	cg.realTime = realTime;
482 
483 	cg.netFrameTime = netFrameTime;
484 	cg.netTime += netFrameTime * 1000.0f;
485 	cg.refreshFrameTime = refreshFrameTime;
486 	cg.refreshTime += refreshFrameTime * 1000.0f;
487 
488 	// Clamp time
489 	cg.netTime = clamp (cg.netTime, cg.frame.serverTime - 100, cg.frame.serverTime);
490 	if (cg.netFrameTime > FRAMETIME_MAX)
491 		cg.netFrameTime = FRAMETIME_MAX;
492 
493 	cg.refreshTime = clamp (cg.refreshTime, cg.frame.serverTime - 100, cg.frame.serverTime);
494 	if (cg.refreshFrameTime > FRAMETIME_MAX)
495 		cg.refreshFrameTime = FRAMETIME_MAX;
496 
497 	// Only update time if we're not rendering the scene
498 	if (!cg.forceRefDef && (cg.mapLoading || !cg.frame.valid || cgi.Com_ClientState () != CA_ACTIVE || !refreshPrepped)) {
499 		// Render the menu
500 		switch (cgi.Com_ClientState ()) {
501 		case CA_DISCONNECTED:
502 			if (cgi.Key_GetDest () == KD_GAME)
503 				CG_DrawConnectScreen ();
504 			UI_Refresh (qTrue);
505 			break;
506 
507 		case CA_CONNECTING:
508 		case CA_CONNECTED:
509 		case CA_ACTIVE:
510 			CG_DrawConnectScreen ();
511 			UI_Refresh (qFalse);
512 			break;
513 		}
514 
515 		return;
516 	}
517 
518 	// Predict all unacknowledged movements
519 	CG_PredictMovement ();
520 
521 	// Watch for gender bending if desired
522 	CG_FixUpGender ();
523 
524 	// Run light styles
525 	CG_RunLightStyles ();
526 
527 	// An invalid frame will just use the exact previous refdef
528 	// We can't use the old frame if the video mode has changed, though
529 	if (cg.frame.valid && (cg.forceRefDef || !cgi.Cvar_GetIntegerValue ("paused"))) {
530 		cg.forceRefDef = qFalse;
531 
532 		cgi.R_ClearScene ();
533 
534 		// Calculate the view values
535 		V_CalcViewValues ();
536 
537 		// Add in entities and effects
538 		CG_AddEntities ();
539 
540 		// Testing testing...
541 		if (cl_testblend->intVal) {
542 			cg.viewBlend[0] = 1.0f;
543 			cg.viewBlend[1] = 0.5f;
544 			cg.viewBlend[2] = 0.25f;
545 			cg.viewBlend[3] = 0.5f;
546 		}
547 		if (cl_testentities->intVal)
548 			V_TestEntities ();
549 		if (cl_testlights->intVal)
550 			V_TestLights ();
551 		if (cl_testparticles->intVal)
552 			V_TestParticles ();
553 
554 		// Offset the viewOrigin if we're using stereo separation
555 		if (stereoSeparation != 0)
556 			Vec3MA (cg.refDef.viewOrigin, stereoSeparation, cg.refDef.rightVec, cg.refDef.viewOrigin);
557 
558 		// Never let it sit exactly on a node line, because a water plane
559 		// can dissapear when viewed with the eye exactly on it. the server
560 		// protocol only specifies to 1/8 pixel, so add 1/16 in each axis
561 		cg.refDef.viewOrigin[0] += (1.0f/16.0f);
562 		cg.refDef.viewOrigin[1] += (1.0f/16.0f);
563 		cg.refDef.viewOrigin[2] += (1.0f/16.0f);
564 
565 		cg.refDef.velocity[0]	= cg.predicted.velocity[0];
566 		cg.refDef.velocity[1]	= cg.predicted.velocity[1];
567 		cg.refDef.velocity[2]	= cg.predicted.velocity[2];
568 
569 		cg.refDef.fovY			= Q_CalcFovY (cg.refDef.fovX, (float)cg.refDef.width, (float)cg.refDef.height);
570 
571 		cg.refDef.time			= cg.refreshTime * 0.001f;
572 
573 		cg.refDef.areaChanged	= cg.frame.areaChanged;
574 		cg.refDef.areaBits		= cg.frame.areaBits;
575 
576 		cg.refDef.rdFlags		= cg.frame.playerState.rdFlags;
577 	}
578 
579 	// Render the frame
580 	cgi.R_RenderScene (&cg.refDef);
581 
582 	// Update orientation for sound subsystem
583 	cgi.Snd_Update (&cg.refDef);
584 
585 	// Run subsystems
586 	CG_RunDLights ();
587 
588 	// Render screen stuff
589 	SCR_Draw ();
590 
591 	// Increment frame counter
592 	cg.frameCount++;
593 }
594 
595 /*
596 =======================================================================
597 
598 	CONSOLE FUNCTIONS
599 
600 =======================================================================
601 */
602 
603 /*
604 =============
605 V_Viewpos_f
606 =============
607 */
V_Viewpos_f(void)608 static void V_Viewpos_f (void)
609 {
610 	Com_Printf (0, "(x%i y%i z%i) : yaw%i\n",
611 				(int)cg.refDef.viewOrigin[0],
612 				(int)cg.refDef.viewOrigin[1],
613 				(int)cg.refDef.viewOrigin[2],
614 				(int)cg.refDef.viewAngles[YAW]);
615 }
616 
617 
618 /*
619 ================
620 V_Benchmark_f
621 ================
622 */
V_Benchmark_f(void)623 static void V_Benchmark_f (void)
624 {
625 	int			i, j, times;
626 	int			start;
627 	float		time, timeinc;
628 	float		result;
629 	refDef_t	refDef;
630 
631 	memset (&refDef, 0, sizeof (refDef_t));
632 
633 	refDef.width = cg.refConfig.vidWidth;
634 	refDef.height = cg.refConfig.vidHeight;
635 	refDef.fovX = 90;
636 	refDef.fovY = Q_CalcFovY (refDef.fovX, refDef.width, refDef.height);
637 	Vec3MA (cg.frame.playerState.viewOffset, (1.0f/8.0f), cg.frame.playerState.pMove.origin, refDef.viewOrigin);
638 
639 	if (cgi.Cmd_Argc () >= 2)
640 		times = atoi (cgi.Cmd_Argv (1));
641 	else
642 		times = 10;
643 
644 	for (j=0, result=0, timeinc=0 ; j<times ; j++) {
645 		start = cgi.Sys_Milliseconds ();
646 
647 		if (cgi.Cmd_Argc () >= 3) {
648 			// Run without page flipping
649 			cgi.R_BeginFrame (0);
650 			for (i=0 ; i<128 ; i++) {
651 				refDef.viewAngles[1] = i/128.0*360.0;
652 				Angles_Matrix3 (refDef.viewAngles, refDef.viewAxis);
653 				Vec3Negate (cg.refDef.viewAxis[1], cg.refDef.rightVec);
654 
655 				cgi.R_RenderScene (&refDef);
656 			}
657 			cgi.R_EndFrame ();
658 		}
659 		else {
660 			for (i=0 ; i<128 ; i++) {
661 				refDef.viewAngles[1] = i/128.0*360.0;
662 				Angles_Matrix3 (refDef.viewAngles, refDef.viewAxis);
663 				Vec3Negate (cg.refDef.viewAxis[1], cg.refDef.rightVec);
664 
665 				cgi.R_BeginFrame (0);
666 				cgi.R_RenderScene (&refDef);
667 				cgi.R_EndFrame ();
668 			}
669 		}
670 
671 		time = (cgi.Sys_Milliseconds () - start) / 1000.0;
672 		timeinc += time;
673 		result += 128.0 / time;
674 	}
675 
676 	Com_Printf (0, "%f secs, %f fps total\n", timeinc, result/times);
677 }
678 
679 
680 /*
681 ================
682 V_TimeRefresh_f
683 ================
684 */
V_TimeRefresh_f(void)685 static void V_TimeRefresh_f (void)
686 {
687 	int			i, start;
688 	float		time;
689 	refDef_t	refDef;
690 
691 	start = cgi.Sys_Milliseconds ();
692 
693 	memset (&refDef, 0, sizeof (refDef_t));
694 
695 	refDef.width = cg.refConfig.vidWidth;
696 	refDef.height = cg.refConfig.vidHeight;
697 	refDef.fovX = 90;
698 	refDef.fovY = Q_CalcFovY (refDef.fovX, refDef.width, refDef.height);
699 	Vec3MA (cg.frame.playerState.viewOffset, (1.0f/8.0f), cg.frame.playerState.pMove.origin, refDef.viewOrigin);
700 
701 	if (cgi.Cmd_Argc () == 2) {
702 		// Run without page flipping
703 		cgi.R_BeginFrame (0);
704 		for (i=0 ; i<128 ; i++) {
705 			refDef.viewAngles[1] = i/128.0*360.0;
706 			Angles_Matrix3 (refDef.viewAngles, refDef.viewAxis);
707 			Vec3Negate (cg.refDef.viewAxis[1], cg.refDef.rightVec);
708 
709 			cgi.R_RenderScene (&refDef);
710 		}
711 		cgi.R_EndFrame ();
712 	}
713 	else {
714 		for (i=0 ; i<128 ; i++) {
715 			refDef.viewAngles[1] = i/128.0*360.0;
716 			Angles_Matrix3 (refDef.viewAngles, refDef.viewAxis);
717 			Vec3Negate (cg.refDef.viewAxis[1], cg.refDef.rightVec);
718 
719 			cgi.R_BeginFrame (0);
720 			cgi.R_RenderScene (&refDef);
721 			cgi.R_EndFrame ();
722 		}
723 	}
724 
725 	time = (cgi.Sys_Milliseconds () - start) / 1000.0;
726 	Com_Printf (0, "%f seconds (%f fps)\n", time, 128/time);
727 }
728 
729 /*
730 =======================================================================
731 
732 	INIT / SHUTDOWN
733 
734 =======================================================================
735 */
736 
737 static void	*cmd_sizeUp;
738 static void	*cmd_sizeDown;
739 static void	*cmd_viewPos;
740 static void	*cmd_benchMark;
741 static void	*cmd_timeRefresh;
742 
743 /*
744 =============
745 V_Register
746 =============
747 */
V_Register(void)748 void V_Register (void)
749 {
750 	cmd_sizeUp		= cgi.Cmd_AddCommand ("sizeup",			V_SizeUp_f,			"Increases viewport size");
751 	cmd_sizeDown	= cgi.Cmd_AddCommand ("sizedown",		V_SizeDown_f,		"Decreases viewport size");
752 
753 	cmd_viewPos		= cgi.Cmd_AddCommand ("viewpos",		V_Viewpos_f,		"Prints view position and yaw");
754 
755 	cmd_benchMark	= cgi.Cmd_AddCommand ("benchmark",		V_Benchmark_f,		"Multiple speed tests for one scene");
756 	cmd_timeRefresh	= cgi.Cmd_AddCommand ("timerefresh",	V_TimeRefresh_f,	"Prints framerate of current scene");
757 }
758 
759 
760 /*
761 =============
762 V_Unregister
763 =============
764 */
V_Unregister(void)765 void V_Unregister (void)
766 {
767 	cgi.Cmd_RemoveCommand ("sizeup", cmd_sizeUp);
768 	cgi.Cmd_RemoveCommand ("sizedown", cmd_sizeDown);
769 
770 	cgi.Cmd_RemoveCommand ("viewpos", cmd_viewPos);
771 
772 	cgi.Cmd_RemoveCommand ("benchmark", cmd_benchMark);
773 	cgi.Cmd_RemoveCommand ("timerefresh", cmd_timeRefresh);
774 }
775