1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 // cg_view.c -- setup all the parameters (position, angle, etc)
25 // for a 3D rendering
26 
27 // this line must stay at top so the whole PCH thing works...
28 #include "cg_headers.h"
29 
30 #include "cg_media.h"
31 #include "FxScheduler.h"
32 #include "../game/wp_saber.h"
33 #include "../game/g_vehicles.h"
34 
35 #define MASK_CAMERACLIP (MASK_SOLID)
36 #define CAMERA_SIZE	4
37 
38 float cg_zoomFov;
39 
40 //#define CG_CAM_ABOVE	2
41 extern qboolean CG_OnMovingPlat( playerState_t *ps );
42 extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
43 
44 extern int g_crosshairSameEntTime;
45 extern int g_crosshairEntNum;
46 
47 /*
48 =============================================================================
49 
50   MODEL TESTING
51 
52 The viewthing and gun positioning tools from Q2 have been integrated and
53 enhanced into a single model testing facility.
54 
55 Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
56 
57 The names must be the full pathname after the basedir, like
58 "models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
59 
60 Testmodel will create a fake entity 100 units in front of the current view
61 position, directly facing the viewer.  It will remain immobile, so you can
62 move around it to view it from different angles.
63 
64 Testgun will cause the model to follow the player around and supress the real
65 view weapon model.  The default frame 0 of most guns is completely off screen,
66 so you will probably have to cycle a couple frames to see it.
67 
68 "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
69 frame or skin of the testmodel.  These are bound to F5, F6, F7, and F8 in
70 q3default.cfg.
71 
72 If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
73 you adjust the positioning.
74 
75 Note that none of the model testing features update while the game is paused, so
76 it may be convenient to test with deathmatch set to 1 so that bringing down the
77 console doesn't pause the game.
78 
79 =============================================================================
80 */
81 
82 /*
83 Ghoul2 Insert Start
84 */
85 
86 /*
87 =================
88 CG_TestModel_f
89 
90 Creates an entity in front of the current position, which
91 can then be moved around
92 =================
93 */
CG_TestG2Model_f(void)94 void CG_TestG2Model_f (void) {
95 	vec3_t		angles;
96 	CGhoul2Info_v *ghoul2;
97 
98 	memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
99 	ghoul2 = new CGhoul2Info_v;
100 	cg.testModelEntity.ghoul2 = ghoul2;
101 	if ( cgi_Argc() < 2 ) {
102 		return;
103 	}
104 
105 	Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
106 	cg.testModelEntity.hModel = cgi_R_RegisterModel( cg.testModelName );
107 
108 	cg.testModel = gi.G2API_InitGhoul2Model(*((CGhoul2Info_v *)cg.testModelEntity.ghoul2), cg.testModelName, cg.testModelEntity.hModel, NULL_HANDLE, NULL_HANDLE,0,0);
109 	cg.testModelEntity.radius = 100.0f;
110 
111 	if ( cgi_Argc() == 3 ) {
112 		cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
113 		cg.testModelEntity.frame = 1;
114 		cg.testModelEntity.oldframe = 0;
115 	}
116 	if (! cg.testModelEntity.hModel ) {
117 		CG_Printf( "Can't register model\n" );
118 		return;
119 	}
120 
121 	VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
122 
123 	angles[PITCH] = 0;
124 	angles[YAW] = 180 + cg.refdefViewAngles[1];
125 	angles[ROLL] = 0;
126 
127 	AnglesToAxis( angles, cg.testModelEntity.axis );
128 }
129 
CG_ListModelSurfaces_f(void)130 void CG_ListModelSurfaces_f (void)
131 {
132 	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)cg.testModelEntity.ghoul2);
133 
134  	gi.G2API_ListSurfaces(&ghoul2[cg.testModel]);
135 }
136 
137 
CG_ListModelBones_f(void)138 void CG_ListModelBones_f (void)
139 {
140   	// test to see if we got enough args
141 	if ( cgi_Argc() < 2 )
142 	{
143 		return;
144 	}
145 	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)cg.testModelEntity.ghoul2);
146 
147 	gi.G2API_ListBones(&ghoul2[cg.testModel], atoi(CG_Argv(1)));
148 }
149 
CG_TestModelSurfaceOnOff_f(void)150 void CG_TestModelSurfaceOnOff_f(void)
151 {
152 	// test to see if we got enough args
153 	if ( cgi_Argc() < 3 )
154 	{
155 		return;
156 	}
157 	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)cg.testModelEntity.ghoul2);
158 
159 	gi.G2API_SetSurfaceOnOff(&ghoul2[cg.testModel], CG_Argv(1), atoi(CG_Argv(2)));
160 }
161 
CG_TestModelSetAnglespre_f(void)162 void CG_TestModelSetAnglespre_f(void)
163 {
164 	vec3_t	angles;
165 
166 	if ( cgi_Argc() < 3 )
167 	{
168 		return;
169 	}
170 	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)cg.testModelEntity.ghoul2);
171 
172 	angles[0] = atof(CG_Argv(2));
173 	angles[1] = atof(CG_Argv(3));
174 	angles[2] = atof(CG_Argv(4));
175 	gi.G2API_SetBoneAngles(&ghoul2[cg.testModel], CG_Argv(1), angles, BONE_ANGLES_PREMULT, POSITIVE_X, POSITIVE_Z, POSITIVE_Y, NULL, 0, 0);
176 }
177 
CG_TestModelSetAnglespost_f(void)178 void CG_TestModelSetAnglespost_f(void)
179 {
180 	vec3_t	angles;
181 
182 	if ( cgi_Argc() < 3 )
183 	{
184 		return;
185 	}
186 	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)cg.testModelEntity.ghoul2);
187 
188 	angles[0] = atof(CG_Argv(2));
189 	angles[1] = atof(CG_Argv(3));
190 	angles[2] = atof(CG_Argv(4));
191 	gi.G2API_SetBoneAngles(&ghoul2[cg.testModel], CG_Argv(1), angles, BONE_ANGLES_POSTMULT, POSITIVE_X, POSITIVE_Z, POSITIVE_Y, NULL, 0, 0);
192 }
193 
CG_TestModelAnimate_f(void)194 void CG_TestModelAnimate_f(void)
195 {
196 	char	boneName[100];
197 	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)cg.testModelEntity.ghoul2);
198 
199 	strcpy(boneName, CG_Argv(1));
200 	gi.G2API_SetBoneAnim(&ghoul2[cg.testModel], boneName, atoi(CG_Argv(2)), atoi(CG_Argv(3)), BONE_ANIM_OVERRIDE_LOOP, atof(CG_Argv(4)), cg.time, -1, -1);
201 
202 }
203 
204 /*
205 Ghoul2 Insert End
206 */
207 
208 
209 /*
210 =================
211 CG_TestModel_f
212 
213 Creates an entity in front of the current position, which
214 can then be moved around
215 =================
216 */
CG_TestModel_f(void)217 void CG_TestModel_f (void) {
218 	vec3_t		angles;
219 
220 	memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
221 	if ( cgi_Argc() < 2 ) {
222 		return;
223 	}
224 
225 	Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
226 	cg.testModelEntity.hModel = cgi_R_RegisterModel( cg.testModelName );
227 
228 	if ( cgi_Argc() == 3 ) {
229 		cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
230 		cg.testModelEntity.frame = 1;
231 		cg.testModelEntity.oldframe = 0;
232 	}
233 	if (! cg.testModelEntity.hModel ) {
234 		CG_Printf( "Can't register model\n" );
235 		return;
236 	}
237 
238 	VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
239 
240 	angles[PITCH] = 0;
241 	angles[YAW] = 180 + cg.refdefViewAngles[1];
242 	angles[ROLL] = 0;
243 
244 	AnglesToAxis( angles, cg.testModelEntity.axis );
245 }
246 
247 
CG_TestModelNextFrame_f(void)248 void CG_TestModelNextFrame_f (void) {
249 	cg.testModelEntity.frame++;
250 	CG_Printf( "frame %i\n", cg.testModelEntity.frame );
251 }
252 
CG_TestModelPrevFrame_f(void)253 void CG_TestModelPrevFrame_f (void) {
254 	cg.testModelEntity.frame--;
255 	if ( cg.testModelEntity.frame < 0 ) {
256 		cg.testModelEntity.frame = 0;
257 	}
258 	CG_Printf( "frame %i\n", cg.testModelEntity.frame );
259 }
260 
CG_TestModelNextSkin_f(void)261 void CG_TestModelNextSkin_f (void) {
262 	cg.testModelEntity.skinNum++;
263 	CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
264 }
265 
CG_TestModelPrevSkin_f(void)266 void CG_TestModelPrevSkin_f (void) {
267 	cg.testModelEntity.skinNum--;
268 	if ( cg.testModelEntity.skinNum < 0 ) {
269 		cg.testModelEntity.skinNum = 0;
270 	}
271 	CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
272 }
273 
CG_AddTestModel(void)274 static void CG_AddTestModel (void) {
275 	// re-register the model, because the level may have changed
276 /*	cg.testModelEntity.hModel = cgi_R_RegisterModel( cg.testModelName );
277 	if (! cg.testModelEntity.hModel ) {
278 		CG_Printf ("Can't register model\n");
279 		return;
280 	}
281 */
282 	cgi_R_AddRefEntityToScene( &cg.testModelEntity );
283 }
284 
285 
286 
287 //============================================================================
288 
289 
290 /*
291 =================
292 CG_CalcVrect
293 
294 Sets the coordinates of the rendered window
295 =================
296 */
CG_CalcVrect(void)297 void CG_CalcVrect (void) {
298 	const int		size = 100;
299 
300 	cg.refdef.width = cgs.glconfig.vidWidth * size * 0.01;
301 	cg.refdef.width &= ~1;
302 
303 	cg.refdef.height = cgs.glconfig.vidHeight * size * 0.01;
304 	cg.refdef.height &= ~1;
305 
306 	cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width) * 0.5;
307 	cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height) * 0.5;
308 }
309 
310 //==============================================================================
311 //==============================================================================
312 #define CAMERA_DAMP_INTERVAL	50
313 
314 #define CAMERA_CROUCH_NUDGE		6
315 
316 static vec3_t	cameramins = { -CAMERA_SIZE, -CAMERA_SIZE, -CAMERA_SIZE };
317 static vec3_t	cameramaxs = { CAMERA_SIZE, CAMERA_SIZE, CAMERA_SIZE };
318 vec3_t	camerafwd, cameraup, camerahorizdir;
319 
320 vec3_t	cameraFocusAngles,			cameraFocusLoc;
321 vec3_t	cameraIdealTarget,			cameraIdealLoc;
322 vec3_t	cameraCurTarget={0,0,0},	cameraCurLoc={0,0,0};
323 vec3_t	cameraOldLoc={0,0,0},		cameraNewLoc={0,0,0};
324 int		cameraLastFrame=0;
325 
326 float	cameraLastYaw=0;
327 float	cameraStiffFactor=0.0f;
328 
329 /*
330 ===============
331 Notes on the camera viewpoint in and out...
332 
333 cg.refdef.vieworg
334 --at the start of the function holds the player actor's origin (center of player model).
335 --it is set to the final view location of the camera at the end of the camera code.
336 cg.refdefViewAngles
337 --at the start holds the client's view angles
338 --it is set to the final view angle of the camera at the end of the camera code.
339 
340 ===============
341 */
342 
343 /*
344 ===============
345 CG_CalcIdealThirdPersonViewTarget
346 
347 ===============
348 */
CG_CalcIdealThirdPersonViewTarget(void)349 static void CG_CalcIdealThirdPersonViewTarget(void)
350 {
351 	// Initialize IdealTarget
352 	qboolean usesViewEntity = (qboolean)(cg.snap->ps.viewEntity && cg.snap->ps.viewEntity < ENTITYNUM_WORLD);
353 	VectorCopy(cg.refdef.vieworg, cameraFocusLoc);
354 
355 	if ( usesViewEntity )
356 	{
357 
358 		gentity_t *gent = &g_entities[cg.snap->ps.viewEntity];
359 		if ( gent->client && (gent->client->NPC_class == CLASS_GONK
360 			|| gent->client->NPC_class == CLASS_INTERROGATOR
361 			|| gent->client->NPC_class == CLASS_SENTRY
362 			|| gent->client->NPC_class == CLASS_PROBE
363 			|| gent->client->NPC_class == CLASS_MOUSE
364 			|| gent->client->NPC_class == CLASS_R2D2
365 			|| gent->client->NPC_class == CLASS_R5D2) )
366 		{	// Droids use a generic offset
367 			cameraFocusLoc[2] += 4;
368 			VectorCopy( cameraFocusLoc, cameraIdealTarget );
369 			return;
370 		}
371 
372 		if( gent->client->ps.pm_flags & PMF_DUCKED )
373 		{	// sort of a nasty hack in order to get this to work. Don't tell Ensiform, or I'll have to kill him. --eez
374 			cameraFocusLoc[2] -= CAMERA_CROUCH_NUDGE*4;
375 		}
376 	}
377 
378 	// Add in the new viewheight
379 	cameraFocusLoc[2] += cg.predicted_player_state.viewheight;
380 	if ( cg.snap
381 		&& (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE) )
382 	{
383 		VectorCopy( cameraFocusLoc, cameraIdealTarget );
384 		cameraIdealTarget[2] += 192;
385 	}
386 	else if ( cg.snap
387 		&& (cg.snap->ps.eFlags&EF_HELD_BY_WAMPA) )
388 	{
389 		VectorCopy( cameraFocusLoc, cameraIdealTarget );
390 		cameraIdealTarget[2] -= 48;
391 	}
392 	else if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_VOF )
393 	{
394 		// Add in a vertical offset from the viewpoint, which puts the actual target above the head, regardless of angle.
395 		VectorCopy( cameraFocusLoc, cameraIdealTarget );
396 		cameraIdealTarget[2] += cg.overrides.thirdPersonVertOffset;
397 		//VectorMA(cameraFocusLoc, cg.overrides.thirdPersonVertOffset, cameraup, cameraIdealTarget);
398 	}
399 	else
400 	{
401 		// Add in a vertical offset from the viewpoint, which puts the actual target above the head, regardless of angle.
402 		VectorCopy( cameraFocusLoc, cameraIdealTarget );
403 		cameraIdealTarget[2] += cg_thirdPersonVertOffset.value;
404 		//VectorMA(cameraFocusLoc, cg_thirdPersonVertOffset.value, cameraup, cameraIdealTarget);
405 	}
406 
407 	// Now, if the player is crouching, do a little special tweak.  The problem is that the player's head is way out of his bbox.
408 	if (cg.predicted_player_state.pm_flags & PMF_DUCKED)
409 	{ // Nudge to focus location up a tad.
410 		vec3_t nudgepos;
411 		trace_t trace;
412 
413 		VectorCopy(cameraFocusLoc, nudgepos);
414 		nudgepos[2]+=CAMERA_CROUCH_NUDGE;
415 		CG_Trace(&trace, cameraFocusLoc, cameramins, cameramaxs, nudgepos,
416 			( usesViewEntity ) ? cg.snap->ps.viewEntity : cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
417 		if (trace.fraction < 1.0)
418 		{
419 			VectorCopy(trace.endpos, cameraFocusLoc);
420 		}
421 		else
422 		{
423 			VectorCopy(nudgepos, cameraFocusLoc);
424 		}
425 	}
426 }
427 
428 
429 
430 /*
431 ===============
432 CG_CalcIdealThirdPersonViewLocation
433 
434 ===============
435 */
CG_CalcIdealThirdPersonViewLocation(void)436 static void CG_CalcIdealThirdPersonViewLocation(void)
437 {
438 	if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_RNG )
439 	{
440 		VectorMA(cameraIdealTarget, -(cg.overrides.thirdPersonRange), camerafwd, cameraIdealLoc);
441 	}
442 	else if ( cg.snap
443 		&& (cg.snap->ps.eFlags&EF_HELD_BY_RANCOR)
444 		&& cg_entities[cg.snap->ps.clientNum].gent->activator )
445 	{//stay back
446 		VectorMA(cameraIdealTarget, -180.0f*cg_entities[cg.snap->ps.clientNum].gent->activator->s.modelScale[0], camerafwd, cameraIdealLoc);
447 	}
448 	else if ( cg.snap
449 		&& (cg.snap->ps.eFlags&EF_HELD_BY_WAMPA)
450 		&& cg_entities[cg.snap->ps.clientNum].gent->activator
451 		&& cg_entities[cg.snap->ps.clientNum].gent->activator->inuse )
452 	{//stay back
453 		VectorMA(cameraIdealTarget, -120.0f*cg_entities[cg.snap->ps.clientNum].gent->activator->s.modelScale[0], camerafwd, cameraIdealLoc);
454 	}
455 	else if ( cg.snap
456 		&& (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
457 		&& cg_entities[cg.snap->ps.clientNum].gent->activator )
458 	{//stay back
459 		VectorMA(cg_entities[cg_entities[cg.snap->ps.clientNum].gent->activator->s.number].lerpOrigin, -180.0f, camerafwd, cameraIdealLoc);
460 	}
461 	else
462 	{
463 		VectorMA(cameraIdealTarget, -(cg_thirdPersonRange.value), camerafwd, cameraIdealLoc);
464 	}
465 
466 	if ( cg.renderingThirdPerson && (cg.snap->ps.forcePowersActive&(1<<FP_SPEED)) && player->client->ps.forcePowerDuration[FP_SPEED] )
467 	{
468 		float timeLeft = player->client->ps.forcePowerDuration[FP_SPEED] - cg.time;
469 		float length = FORCE_SPEED_DURATION*forceSpeedValue[player->client->ps.forcePowerLevel[FP_SPEED]];
470 		float amt = forceSpeedRangeMod[player->client->ps.forcePowerLevel[FP_SPEED]];
471 		if ( timeLeft < 500 )
472 		{//start going back
473 			VectorMA(cameraIdealLoc, (timeLeft)/500*amt, camerafwd, cameraIdealLoc);
474 		}
475 		else if ( length - timeLeft < 1000 )
476 		{//start zooming in
477 			VectorMA(cameraIdealLoc, (length - timeLeft)/1000*amt, camerafwd, cameraIdealLoc);
478 		}
479 		else
480 		{
481 			VectorMA(cameraIdealLoc, amt, camerafwd, cameraIdealLoc);
482 		}
483 	}
484 }
485 
486 
487 
CG_ResetThirdPersonViewDamp(void)488 static void CG_ResetThirdPersonViewDamp(void)
489 {
490 	trace_t trace;
491 
492 	// Cap the pitch within reasonable limits
493 	if (cameraFocusAngles[PITCH] > 89.0)
494 	{
495 		cameraFocusAngles[PITCH] = 89.0;
496 	}
497 	else if (cameraFocusAngles[PITCH] < -89.0)
498 	{
499 		cameraFocusAngles[PITCH] = -89.0;
500 	}
501 
502 	AngleVectors(cameraFocusAngles, camerafwd, NULL, cameraup);
503 
504 	// Set the cameraIdealTarget
505 	CG_CalcIdealThirdPersonViewTarget();
506 
507 	// Set the cameraIdealLoc
508 	CG_CalcIdealThirdPersonViewLocation();
509 
510 	// Now, we just set everything to the new positions.
511 	VectorCopy(cameraIdealLoc, cameraCurLoc);
512 	VectorCopy(cameraIdealTarget, cameraCurTarget);
513 
514 	// First thing we do is trace from the first person viewpoint out to the new target location.
515 	CG_Trace(&trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurTarget, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
516 	if (trace.fraction <= 1.0)
517 	{
518 		VectorCopy(trace.endpos, cameraCurTarget);
519 	}
520 
521 	// Now we trace from the new target location to the new view location, to make sure there is nothing in the way.
522 	CG_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
523 	if (trace.fraction <= 1.0)
524 	{
525 		VectorCopy(trace.endpos, cameraCurLoc);
526 	}
527 
528 	cameraLastFrame = cg.time;
529 	cameraLastYaw = cameraFocusAngles[YAW];
530 	cameraStiffFactor = 0.0f;
531 }
532 
533 // This is called every frame.
CG_UpdateThirdPersonTargetDamp(void)534 static void CG_UpdateThirdPersonTargetDamp(void)
535 {
536 	trace_t trace;
537 	vec3_t	targetdiff;
538 	float	dampfactor, dtime, ratio;
539 
540 	// Set the cameraIdealTarget
541 	// Automatically get the ideal target, to avoid jittering.
542 	CG_CalcIdealThirdPersonViewTarget();
543 
544 	if ( CG_OnMovingPlat( &cg.snap->ps ) )
545 	{//if moving on a plat, camera is *tight*
546 		VectorCopy(cameraIdealTarget, cameraCurTarget);
547 	}
548 	else if (cg_thirdPersonTargetDamp.value>=1.0)//||cg.thisFrameTeleport)
549 	{	// No damping.
550 		VectorCopy(cameraIdealTarget, cameraCurTarget);
551 	}
552 	else if (cg_thirdPersonTargetDamp.value>=0.0)
553 	{
554 		// Calculate the difference from the current position to the new one.
555 		VectorSubtract(cameraIdealTarget, cameraCurTarget, targetdiff);
556 
557 		// Now we calculate how much of the difference we cover in the time allotted.
558 		// The equation is (Damp)^(time)
559 		dampfactor = 1.0-cg_thirdPersonTargetDamp.value;	// We must exponent the amount LEFT rather than the amount bled off
560 		dtime = (float)(cg.time-cameraLastFrame) * (1.0/cg_timescale.value) * (1.0/(float)CAMERA_DAMP_INTERVAL);	// Our dampfactor is geared towards a time interval equal to "1".
561 
562 		// Note that since there are a finite number of "practical" delta millisecond values possible,
563 		// the ratio should be initialized into a chart ultimately.
564 		if ( cg_smoothCamera.integer )
565 			ratio = powf( dampfactor, dtime );
566 		else
567 			ratio = Q_powf( dampfactor, dtime );
568 
569 		// This value is how much distance is "left" from the ideal.
570 		VectorMA(cameraIdealTarget, -ratio, targetdiff, cameraCurTarget);
571 		/////////////////////////////////////////////////////////////////////////////////////////////////////////
572 	}
573 
574 	// Now we trace to see if the new location is cool or not.
575 
576 	// First thing we do is trace from the first person viewpoint out to the new target location.
577 	if ( cg.snap
578 		&& (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
579 		&& cg_entities[cg.snap->ps.clientNum].gent->activator )
580 	{//if being held by a sand creature, trace from his actual origin, since we could be underground or otherwise in solid once he eats us
581 		CG_Trace(&trace, cg_entities[cg_entities[cg.snap->ps.clientNum].gent->activator->s.number].lerpOrigin, cameramins, cameramaxs, cameraCurTarget, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
582 	}
583 	else
584 	{
585 		CG_Trace(&trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurTarget, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
586 	}
587 	if (trace.fraction < 1.0)
588 	{
589 		VectorCopy(trace.endpos, cameraCurTarget);
590 	}
591 
592 	// Note that previously there was an upper limit to the number of physics traces that are done through the world
593 	// for the sake of camera collision, since it wasn't calced per frame.  Now it is calculated every frame.
594 	// This has the benefit that the camera is a lot smoother now (before it lerped between tested points),
595 	// however two full volume traces each frame is a bit scary to think about.
596 }
597 
598 // This can be called every interval, at the user's discretion.
599 static int camWaterAdjust = 0;
CG_UpdateThirdPersonCameraDamp(void)600 static void CG_UpdateThirdPersonCameraDamp(void)
601 {
602 	trace_t trace;
603 	vec3_t	locdiff;
604 	float dampfactor, dtime, ratio;
605 
606 	// Set the cameraIdealLoc
607 	CG_CalcIdealThirdPersonViewLocation();
608 
609 
610 	// First thing we do is calculate the appropriate damping factor for the camera.
611 	dampfactor=0.0f;
612 	if ( CG_OnMovingPlat( &cg.snap->ps ) )
613 	{//if moving on a plat, camera is *tight*
614 		dampfactor=1.0f;
615 	}
616 	else if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_CDP )
617 	{
618 		if ( cg.overrides.thirdPersonCameraDamp != 0.0f )
619 		{
620 			float pitch;
621 
622 			// Note that the camera pitch has already been capped off to 89.
623 			pitch = Q_fabs(cameraFocusAngles[PITCH]);
624 
625 			// The higher the pitch, the larger the factor, so as you look up, it damps a lot less.
626 			pitch /=115.0f;
627 			dampfactor = (1.0-cg.overrides.thirdPersonCameraDamp)*(pitch*pitch);
628 
629 			dampfactor += cg.overrides.thirdPersonCameraDamp;
630 		}
631 	}
632 	else if ( cg_thirdPersonCameraDamp.value != 0.0f )
633 	{
634 		float pitch;
635 
636 		// Note that the camera pitch has already been capped off to 89.
637 		pitch = Q_fabs(cameraFocusAngles[PITCH]);
638 
639 		// The higher the pitch, the larger the factor, so as you look up, it damps a lot less.
640 		pitch /= 115.0f;
641 		dampfactor = (1.0-cg_thirdPersonCameraDamp.value)*(pitch*pitch);
642 
643 		dampfactor += cg_thirdPersonCameraDamp.value;
644 
645 		// Now we also multiply in the stiff factor, so that faster yaw changes are stiffer.
646 		if (cameraStiffFactor > 0.0f)
647 		{	// The cameraStiffFactor is how much of the remaining damp below 1 should be shaved off, i.e. approach 1 as stiffening increases.
648 			dampfactor += (1.0-dampfactor)*cameraStiffFactor;
649 		}
650 	}
651 
652 	if (dampfactor>=1.0)//||cg.thisFrameTeleport)
653 	{	// No damping.
654 		VectorCopy(cameraIdealLoc, cameraCurLoc);
655 	}
656 	else if (dampfactor>=0.0)
657 	{
658 		// Calculate the difference from the current position to the new one.
659 		VectorSubtract(cameraIdealLoc, cameraCurLoc, locdiff);
660 
661 		// Now we calculate how much of the difference we cover in the time allotted.
662 		// The equation is (Damp)^(time)
663 		dampfactor = 1.0-dampfactor;	// We must exponent the amount LEFT rather than the amount bled off
664 		dtime = (float)(cg.time-cameraLastFrame) * (1.0/cg_timescale.value) * (1.0/(float)CAMERA_DAMP_INTERVAL);	// Our dampfactor is geared towards a time interval equal to "1".
665 
666 		// Note that since there are a finite number of "practical" delta millisecond values possible,
667 		// the ratio should be initialized into a chart ultimately.
668 		if ( cg_smoothCamera.integer )
669 			ratio = powf( dampfactor, dtime );
670 		else
671 			ratio = Q_powf( dampfactor, dtime );
672 
673 		// This value is how much distance is "left" from the ideal.
674 		VectorMA(cameraIdealLoc, -ratio, locdiff, cameraCurLoc);
675 		/////////////////////////////////////////////////////////////////////////////////////////////////////////
676 	}
677 
678 	// Now we trace from the first person viewpoint to the new view location, to make sure there is nothing in the way between the user and the camera...
679 //	CG_Trace(&trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurLoc, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
680 	// (OLD) Now we trace from the new target location to the new view location, to make sure there is nothing in the way.
681 	if ( cg.snap
682 		&& (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
683 		&& cg_entities[cg.snap->ps.clientNum].gent->activator )
684 	{//if being held by a sand creature, trace from his actual origin, since we could be underground or otherwise in solid once he eats us
685 		CG_Trace( &trace, cg_entities[cg_entities[cg.snap->ps.clientNum].gent->activator->s.number].lerpOrigin, cameramins, cameramaxs, cameraCurLoc, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
686 	}
687 	else
688 	{
689 		CG_Trace( &trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
690 	}
691 	if ( trace.fraction < 1.0f )
692 	{
693 		VectorCopy( trace.endpos, cameraCurLoc );
694 
695 		// We didn't trace all the way back, so push down the target accordingly.
696 //		VectorSubtract(cameraCurTarget, cameraFocusLoc, locdiff);
697 //		VectorMA(cameraFocusLoc, trace.fraction, locdiff, cameraCurTarget);
698 
699 		//FIXME: when the trace hits movers, it gets very very jaggy... ?
700 		/*
701 		//this doesn't actually help any
702 		if ( trace.entityNum != ENTITYNUM_WORLD )
703 		{
704 			centity_t *cent = &cg_entities[trace.entityNum];
705 			gentity_t *gent = &g_entities[trace.entityNum];
706 			if ( cent != NULL && gent != NULL )
707 			{
708 				if ( cent->currentState.pos.trType == TR_LINEAR || cent->currentState.pos.trType == TR_LINEAR_STOP )
709 				{
710 					vec3_t	diff;
711 					VectorSubtract( cent->lerpOrigin, gent->currentOrigin, diff );
712 					VectorAdd( cameraCurLoc, diff, cameraCurLoc );
713 				}
714 			}
715 		}
716 		*/
717 	}
718 
719 	// Note that previously there was an upper limit to the number of physics traces that are done through the world
720 	// for the sake of camera collision, since it wasn't calced per frame.  Now it is calculated every frame.
721 	// This has the benefit that the camera is a lot smoother now (before it lerped between tested points),
722 	// however two full volume traces each frame is a bit scary to think about.
723 }
724 
725 
726 
727 
728 /*
729 ===============
730 CG_OffsetThirdPersonView
731 
732 ===============
733 */
734 extern qboolean	MatrixMode;
CG_OffsetThirdPersonView(void)735 static void CG_OffsetThirdPersonView( void )
736 {
737 	vec3_t diff;
738 	float deltayaw;
739 
740 	camWaterAdjust = 0;
741 	cameraStiffFactor = 0.0;
742 
743 	// Set camera viewing direction.
744 	VectorCopy( cg.refdefViewAngles, cameraFocusAngles );
745 
746 	if ( cg.snap
747 		&& (cg.snap->ps.eFlags&EF_HELD_BY_RANCOR)
748 		&& cg_entities[cg.snap->ps.clientNum].gent->activator )
749 	{
750 		centity_t	*monster = &cg_entities[cg_entities[cg.snap->ps.clientNum].gent->activator->s.number];
751 		VectorSet( cameraFocusAngles, 0, AngleNormalize180(monster->lerpAngles[YAW]+180), 0 );
752 	}
753 	else if ( cg.snap && (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE) )
754 	{
755 		centity_t	*monster = &cg_entities[cg_entities[cg.snap->ps.clientNum].gent->activator->s.number];
756 		VectorSet( cameraFocusAngles, 0, AngleNormalize180(monster->lerpAngles[YAW]+180), 0 );
757 		cameraFocusAngles[PITCH] = 0.0f;//flatten it out
758 	}
759 	else if ( G_IsRidingVehicle( &g_entities[0] ) )
760 	{
761 		cameraFocusAngles[YAW] = cg_entities[g_entities[0].owner->s.number].lerpAngles[YAW];
762 		if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_ANG )
763 		{
764 			cameraFocusAngles[YAW] += cg.overrides.thirdPersonAngle;
765 		}
766 		else
767 		{
768 			cameraFocusAngles[YAW] += cg_thirdPersonAngle.value;
769 		}
770 	}
771 	else if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 )
772 	{// if dead, look at killer
773 		if ( MatrixMode )
774 		{
775 			if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_ANG )
776 			{
777 				cameraFocusAngles[YAW] += cg.overrides.thirdPersonAngle;
778 			}
779 			else
780 			{
781 				cameraFocusAngles[YAW] = cg.predicted_player_state.stats[STAT_DEAD_YAW];
782 				cameraFocusAngles[YAW] += cg_thirdPersonAngle.value;
783 			}
784 		}
785 		else
786 		{
787 			cameraFocusAngles[YAW] = cg.predicted_player_state.stats[STAT_DEAD_YAW];
788 		}
789 	}
790 	else
791 	{	// Add in the third Person Angle.
792 		if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_ANG )
793 		{
794 			cameraFocusAngles[YAW] += cg.overrides.thirdPersonAngle;
795 		}
796 		else
797 		{
798 			cameraFocusAngles[YAW] += cg_thirdPersonAngle.value;
799 		}
800 		if ( cg.overrides.active & CG_OVERRIDE_3RD_PERSON_POF )
801 		{
802 			cameraFocusAngles[PITCH] += cg.overrides.thirdPersonPitchOffset;
803 		}
804 		else
805 		{
806 			cameraFocusAngles[PITCH] += cg_thirdPersonPitchOffset.value;
807 		}
808 	}
809 
810 	if ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) )
811 	{// First person saber
812 		// FIXME: use something network-friendly
813 		vec3_t	org, viewDir;
814 		VectorCopy( cg_entities[0].gent->client->renderInfo.eyePoint, org );
815 		float blend = 1.0f - fabs(cg.refdefViewAngles[PITCH])/90.0f;
816 		AngleVectors( cg.refdefViewAngles, viewDir, NULL, NULL );
817 		VectorMA( org, -8, viewDir, org );
818 		VectorScale( org, 1.0f - blend, org );
819 		VectorMA( org, blend, cg.refdef.vieworg, cg.refdef.vieworg );
820 		return;
821 	}
822 	// The next thing to do is to see if we need to calculate a new camera target location.
823 
824 	// If we went back in time for some reason, or if we just started, reset the sample.
825 	if (cameraLastFrame == 0 || cameraLastFrame > cg.time)
826 	{
827 		CG_ResetThirdPersonViewDamp();
828 	}
829 	else
830 	{
831 		// Cap the pitch within reasonable limits
832 		if (cameraFocusAngles[PITCH] > 89.0)
833 		{
834 			cameraFocusAngles[PITCH] = 89.0;
835 		}
836 		else if (cameraFocusAngles[PITCH] < -89.0)
837 		{
838 			cameraFocusAngles[PITCH] = -89.0;
839 		}
840 
841 		AngleVectors(cameraFocusAngles, camerafwd, NULL, cameraup);
842 
843 		deltayaw = fabs(cameraFocusAngles[YAW] - cameraLastYaw);
844 		if (deltayaw > 180.0f)
845 		{ // Normalize this angle so that it is between 0 and 180.
846 			deltayaw = fabs(deltayaw - 360.0f);
847 		}
848 		cameraStiffFactor = deltayaw / (float)(cg.time-cameraLastFrame);
849 		if (cameraStiffFactor < 1.0)
850 		{
851 			cameraStiffFactor = 0.0;
852 		}
853 		else if (cameraStiffFactor > 2.5)
854 		{
855 			cameraStiffFactor = 0.75;
856 		}
857 		else
858 		{	// 1 to 2 scales from 0.0 to 0.5
859 			cameraStiffFactor = (cameraStiffFactor-1.0f)*0.5f;
860 		}
861 		cameraLastYaw = cameraFocusAngles[YAW];
862 
863 		// Move the target to the new location.
864 		CG_UpdateThirdPersonTargetDamp();
865 		CG_UpdateThirdPersonCameraDamp();
866 	}
867 
868 	// Now interestingly, the Quake method is to calculate a target focus point above the player, and point the camera at it.
869 	// We won't do that for now.
870 
871 	// We must now take the angle taken from the camera target and location.
872 	VectorSubtract(cameraCurTarget, cameraCurLoc, diff);
873 	//Com_Printf( "%s\n", vtos(diff) );
874 	float dist = VectorNormalize(diff);
875 	if ( dist < 1.0f )
876 	{//must be hitting something, need some value to calc angles, so use cam forward
877 		VectorCopy( camerafwd, diff );
878 	}
879 	vectoangles(diff, cg.refdefViewAngles);
880 
881 	// Temp: just move the camera to the side a bit
882 	extern vmCvar_t cg_thirdPersonHorzOffset;
883 	if ( cg_thirdPersonHorzOffset.value != 0.0f )
884 	{
885 		AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
886 		VectorMA( cameraCurLoc, cg_thirdPersonHorzOffset.value, cg.refdef.viewaxis[1], cameraCurLoc );
887 	}
888 
889 	// ...and of course we should copy the new view location to the proper spot too.
890 	VectorCopy(cameraCurLoc, cg.refdef.vieworg);
891 
892 	//if we hit the water, do a last-minute adjustment
893 	if ( camWaterAdjust )
894 	{
895 		cg.refdef.vieworg[2] += camWaterAdjust;
896 	}
897 	cameraLastFrame=cg.time;
898 }
899 
900 
901 
902 /*
903 ===============
904 CG_OffsetThirdPersonView
905 
906 ===============
907 */
908 /*
909 #define	FOCUS_DISTANCE	512
910 static void CG_OffsetThirdPersonView( void ) {
911 	vec3_t		forward, right, up;
912 	vec3_t		view;
913 	vec3_t		focusAngles;
914 	trace_t		trace;
915 	static vec3_t	mins = { -4, -4, -4 };
916 	static vec3_t	maxs = { 4, 4, 4 };
917 	vec3_t		focusPoint;
918 	float		focusDist;
919 	float		forwardScale, sideScale;
920 
921 	cg.refdef.vieworg[2] += cg.predicted_player_state.viewheight;
922 
923 	VectorCopy( cg.refdefViewAngles, focusAngles );
924 
925 	// if dead, look at killer
926 	if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 ) {
927 		focusAngles[YAW] = cg.predicted_player_state.stats[STAT_DEAD_YAW];
928 		cg.refdefViewAngles[YAW] = cg.predicted_player_state.stats[STAT_DEAD_YAW];
929 	}
930 
931 	if ( focusAngles[PITCH] > 45 ) {
932 		focusAngles[PITCH] = 45;		// don't go too far overhead
933 	}
934 	AngleVectors( focusAngles, forward, NULL, NULL );
935 
936 	VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
937 
938 	VectorCopy( cg.refdef.vieworg, view );
939 
940 	view[2] += 8;
941 
942 	cg.refdefViewAngles[PITCH] *= 0.5;
943 
944 	AngleVectors( cg.refdefViewAngles, forward, right, up );
945 
946 	float tpAngle = cg.overrides.thirdPersonAngle ? cg.overrides.thirdPersonAngle : cg_thirdPersonAngle.value;
947 	forwardScale = cos( tpAngle / 180 * M_PI );
948 	sideScale = sin( tpAngle / 180 * M_PI );
949 	VectorMA( view, -tpAngle * forwardScale, forward, view );
950 	VectorMA( view, -tpAngle * sideScale, right, view );
951 
952 	// trace a ray from the origin to the viewpoint to make sure the view isn't
953 	// in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything
954 
955 	CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predicted_player_state.clientNum, MASK_CAMERACLIP );
956 
957 	if ( trace.fraction != 1.0 ) {
958 		VectorCopy( trace.endpos, view );
959 		view[2] += (1.0 - trace.fraction) * 32;
960 		// try another trace to this position, because a tunnel may have the ceiling
961 		// close enogh that this is poking out
962 
963 		CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predicted_player_state.clientNum, MASK_CAMERACLIP );
964 		VectorCopy( trace.endpos, view );
965 	}
966 
967 
968 	VectorCopy( view, cg.refdef.vieworg );
969 
970 	// select pitch to look at focus point from vieword
971 	VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
972 	focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
973 	if ( focusDist < 1 ) {
974 		focusDist = 1;	// should never happen
975 	}
976 	cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
977 	cg.refdefViewAngles[YAW] -= tpAngle;
978 }
979 
980 
981 #define MIN_CAMERA_HEIGHT	75
982 #define MIN_CAMERA_PITCH	40
983 #define MAX_CAMERA_PITCH	90
984 
985 //----------------------------------------------
986 static void CG_OffsetThirdPersonOverheadView( void ) {
987 	vec3_t		view, angs;
988 	trace_t		trace;
989 	static vec3_t	mins = { -4, -4, -4 };
990 	static vec3_t	maxs = { 4, 4, 4 };
991 
992 	VectorCopy( cg.refdef.vieworg, view );
993 
994 	// Move straight up from the player, making sure to always go at least the min camera height,
995 	//	otherwise, the camera will clip into the head of the player.
996 	float tpRange = cg.overrides.thirdPersonRange ? cg.overrides.thirdPersonRange : cg_thirdPersonRange.value;
997 	if ( tpRange < MIN_CAMERA_HEIGHT )
998 	{
999 		view[2] += MIN_CAMERA_HEIGHT;
1000 	}
1001 	else
1002 	{
1003 		view[2] += tpRange;
1004 	}
1005 
1006 	// Now adjust the camera angles, but we shouldn't adjust the viewAngles...only the viewAxis
1007 	VectorCopy( cg.refdefViewAngles, angs );
1008 	angs[PITCH] = cg.overrides.thirdPersonAngle ? cg.overrides.thirdPersonAngle : cg_thirdPersonAngle.value;
1009 
1010 	// Simple clamp to weed out any really obviously nasty angles
1011 	if ( angs[PITCH] < MIN_CAMERA_PITCH )
1012 	{
1013 		angs[PITCH] = MIN_CAMERA_PITCH;
1014 	}
1015 	else if ( angs[PITCH] > MAX_CAMERA_PITCH )
1016 	{
1017 		angs[PITCH] = MAX_CAMERA_PITCH;
1018 	}
1019 
1020 	// Convert our new desired camera angles and store them where they will get used by the engine
1021 	//	when setting up the actual camera view.
1022 	AnglesToAxis( angs, cg.refdef.viewaxis );
1023 	cg.refdefViewAngles[PITCH] = 0;
1024 	g_entities[0].client->ps.delta_angles[PITCH] = 0;
1025 
1026 	// Trace a ray from the origin to the viewpoint to make sure the view isn't
1027 	//	in a solid block.
1028 	CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predicted_player_state.clientNum, MASK_CAMERACLIP);
1029 
1030 	if ( trace.fraction != 1.0 )
1031 	{
1032 		VectorCopy( trace.endpos, cg.refdef.vieworg );
1033 	}
1034 	else
1035 	{
1036 		VectorCopy( view, cg.refdef.vieworg );
1037 	}
1038 }
1039 */
1040 // this causes a compiler bug on mac MrC compiler
CG_StepOffset(void)1041 static void CG_StepOffset( void ) {
1042 	int		timeDelta;
1043 
1044 	// smooth out stair climbing
1045 	timeDelta = cg.time - cg.stepTime;
1046 	if ( timeDelta < STEP_TIME ) {
1047 		cg.refdef.vieworg[2] -= cg.stepChange
1048 			* (STEP_TIME - timeDelta) / STEP_TIME;
1049 	}
1050 }
1051 
1052 /*
1053 ===============
1054 CG_OffsetFirstPersonView
1055 
1056 ===============
1057 */
1058 extern qboolean PM_InForceGetUp( playerState_t *ps );
1059 extern qboolean PM_InGetUp( playerState_t *ps );
1060 extern qboolean PM_InKnockDown( playerState_t *ps );
1061 extern int PM_AnimLength( int index, animNumber_t anim );
CG_OffsetFirstPersonView(qboolean firstPersonSaber)1062 static void CG_OffsetFirstPersonView( qboolean firstPersonSaber ) {
1063 	float			*origin;
1064 	float			*angles;
1065 	float			bob;
1066 	float			ratio;
1067 	float			delta;
1068 	float			speed;
1069 	float			f;
1070 	vec3_t			predictedVelocity;
1071 	int				timeDelta;
1072 
1073 	if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
1074 		return;
1075 	}
1076 
1077 	origin = cg.refdef.vieworg;
1078 	angles = cg.refdefViewAngles;
1079 
1080 	// if dead, fix the angle and don't add any kick
1081 	if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 )
1082 	{
1083 		angles[ROLL] = 40;
1084 		angles[PITCH] = -15;
1085 		angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
1086 		origin[2] += cg.predicted_player_state.viewheight;
1087 		return;
1088 	}
1089 
1090 	if ( g_entities[0].client && PM_InKnockDown( &g_entities[0].client->ps ) )
1091 	{
1092 		float perc, animLen = (float)PM_AnimLength( g_entities[0].client->clientInfo.animFileIndex, (animNumber_t)g_entities[0].client->ps.legsAnim );
1093 		if ( PM_InGetUp( &g_entities[0].client->ps ) || PM_InForceGetUp( &g_entities[0].client->ps ) )
1094 		{//start righting the view
1095 			perc = (float)g_entities[0].client->ps.legsAnimTimer/animLen*2;
1096 		}
1097 		else
1098 		{//tilt the view
1099 			perc = (animLen-g_entities[0].client->ps.legsAnimTimer)/animLen*2;
1100 		}
1101 		if ( perc > 1.0f )
1102 		{
1103 			perc = 1.0f;
1104 		}
1105 		angles[ROLL] = perc*40;
1106 		angles[PITCH] = perc*-15;
1107 	}
1108 
1109 	// add angles based on weapon kick
1110 	int kickTime = (cg.time - cg.kick_time);
1111 	if ( kickTime < 800 )
1112 	{//kicks are always 1 second long.  Deal with it.
1113 		float kickPerc = 0.0f;
1114 		if ( kickTime <= 200 )
1115 		{//winding up
1116 			kickPerc = kickTime/200.0f;
1117 		}
1118 		else
1119 		{//returning to normal
1120 			kickTime = 800 - kickTime;
1121 			kickPerc = kickTime/600.0f;
1122 		}
1123 		VectorMA( angles, kickPerc, cg.kick_angles, angles );
1124 	}
1125 
1126 	// add angles based on damage kick
1127 	if ( cg.damageTime ) {
1128 		ratio = cg.time - cg.damageTime;
1129 		if ( ratio < DAMAGE_DEFLECT_TIME ) {
1130 			ratio /= DAMAGE_DEFLECT_TIME;
1131 			angles[PITCH] += ratio * cg.v_dmg_pitch;
1132 			angles[ROLL] += ratio * cg.v_dmg_roll;
1133 		} else {
1134 			ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
1135 			if ( ratio > 0 ) {
1136 				angles[PITCH] += ratio * cg.v_dmg_pitch;
1137 				angles[ROLL] += ratio * cg.v_dmg_roll;
1138 			}
1139 		}
1140 	}
1141 
1142 	// add pitch based on fall kick
1143 #if 0
1144 	ratio = ( cg.time - cg.landTime) / FALL_TIME;
1145 	if (ratio < 0)
1146 		ratio = 0;
1147 	angles[PITCH] += ratio * cg.fall_value;
1148 #endif
1149 
1150 	// add angles based on velocity
1151 	VectorCopy( cg.predicted_player_state.velocity, predictedVelocity );
1152 
1153 	delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
1154 	angles[PITCH] += delta * cg_runpitch.value;
1155 
1156 	delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
1157 	angles[ROLL] -= delta * cg_runroll.value;
1158 
1159 	// add angles based on bob
1160 
1161 	// make sure the bob is visible even at low speeds
1162 	speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
1163 
1164 	delta = cg.bobfracsin * cg_bobpitch.value * speed;
1165 	if (cg.predicted_player_state.pm_flags & PMF_DUCKED)
1166 		delta *= 3;		// crouching
1167 	angles[PITCH] += delta;
1168 	delta = cg.bobfracsin * cg_bobroll.value * speed;
1169 	if (cg.predicted_player_state.pm_flags & PMF_DUCKED)
1170 		delta *= 3;		// crouching accentuates roll
1171 	if (cg.bobcycle & 1)
1172 		delta = -delta;
1173 	angles[ROLL] += delta;
1174 
1175 //===================================
1176 
1177 	if ( !firstPersonSaber )//First person saber
1178 	{
1179 		// add view height
1180 		if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
1181 		{
1182 			if ( &g_entities[cg.snap->ps.viewEntity] &&
1183 				g_entities[cg.snap->ps.viewEntity].client &&
1184 				g_entities[cg.snap->ps.viewEntity].client->ps.viewheight )
1185 			{
1186 				origin[2] += g_entities[cg.snap->ps.viewEntity].client->ps.viewheight;
1187 			}
1188 			else
1189 			{
1190 				origin[2] += 4;//???
1191 			}
1192 		}
1193 		else
1194 		{
1195 			origin[2] += cg.predicted_player_state.viewheight;
1196 		}
1197 	}
1198 
1199 	// smooth out duck height changes
1200 	timeDelta = cg.time - cg.duckTime;
1201 	if ( timeDelta < DUCK_TIME) {
1202 		cg.refdef.vieworg[2] -= cg.duckChange * (DUCK_TIME - timeDelta) / DUCK_TIME;
1203 	}
1204 
1205 	// add bob height
1206 	bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
1207 	if (bob > 6) {
1208 		bob = 6;
1209 	}
1210 
1211 	origin[2] += bob;
1212 
1213 
1214 	// add fall height
1215 	delta = cg.time - cg.landTime;
1216 	if ( delta < LAND_DEFLECT_TIME ) {
1217 		f = delta / LAND_DEFLECT_TIME;
1218 		cg.refdef.vieworg[2] += cg.landChange * f;
1219 	} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
1220 		delta -= LAND_DEFLECT_TIME;
1221 		f = 1.0 - ( delta / LAND_RETURN_TIME );
1222 		cg.refdef.vieworg[2] += cg.landChange * f;
1223 	}
1224 
1225 	// add step offset
1226 	CG_StepOffset();
1227 
1228 	if(cg.snap->ps.leanofs != 0)
1229 	{
1230 		vec3_t	right;
1231 		//add leaning offset
1232 		//FIXME: when crouching, this bounces up and down?!
1233 		cg.refdefViewAngles[2] += (float)cg.snap->ps.leanofs/2;
1234 		AngleVectors(cg.refdefViewAngles, NULL, right, NULL);
1235 		VectorMA(cg.refdef.vieworg, (float)cg.snap->ps.leanofs, right, cg.refdef.vieworg);
1236 	}
1237 
1238 	// pivot the eye based on a neck length
1239 #if 0
1240 	{
1241 #define	NECK_LENGTH		8
1242 	vec3_t			forward, up;
1243 
1244 	cg.refdef.vieworg[2] -= NECK_LENGTH;
1245 	AngleVectors( cg.refdefViewAngles, forward, NULL, up );
1246 	VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
1247 	VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
1248 	}
1249 #endif
1250 }
1251 
1252 
1253 /*
1254 ====================
1255 CG_CalcFovFromX
1256 
1257 Calcs Y FOV from given X FOV
1258 ====================
1259 */
CG_CalcFOVFromX(float fov_x)1260 qboolean CG_CalcFOVFromX( float fov_x )
1261 {
1262 	float	x;
1263 	float	fov_y;
1264 	qboolean	inwater;
1265 
1266 	if ( cg_fovAspectAdjust.integer ) {
1267 		// Based on LordHavoc's code for Darkplaces
1268 		// http://www.quakeworld.nu/forum/topic/53/what-does-your-qw-look-like/page/30
1269 		const float baseAspect = 0.75f; // 3/4
1270 		const float aspect = (float)cgs.glconfig.vidWidth/(float)cgs.glconfig.vidHeight;
1271 		const float desiredFov = fov_x;
1272 
1273 		fov_x = atan( tan( desiredFov*M_PI / 360.0f ) * baseAspect*aspect )*360.0f / M_PI;
1274 	}
1275 
1276 	x = cg.refdef.width / tan( fov_x / 360 * M_PI );
1277 	fov_y = atan2( cg.refdef.height, x );
1278 	fov_y = fov_y * 360 / M_PI;
1279 
1280 	// there's a problem with this, it only takes the leafbrushes into account, not the entity brushes,
1281 	//	so if you give slime/water etc properties to a func_door area brush in order to move the whole water
1282 	//	level up/down this doesn't take into account the door position, so warps the view the whole time
1283 	//	whether the water is up or not.
1284 	// warp if underwater
1285 	float	phase;
1286 	float	v;
1287 	cg.refdef.viewContents = 0;
1288 	if (gi.totalMapContents() & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ))
1289 	{
1290 		cg.refdef.viewContents = CG_PointContents( cg.refdef.vieworg, -1 );
1291 	}
1292 	if ( cg.refdef.viewContents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
1293 	{
1294 		phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
1295 		v = WAVE_AMPLITUDE * sin( phase );
1296 		fov_x += v;
1297 		fov_y -= v;
1298 		inwater = qtrue;
1299 	}
1300 	else
1301 	{
1302 		inwater = qfalse;
1303 	}
1304 
1305 	// see if we are drugged by an interrogator.  We muck with the FOV here, a bit later, after viewangles are calc'ed, I muck with those too.
1306 	if ( cg.wonkyTime > 0 && cg.wonkyTime > cg.time )
1307 	{
1308 		float perc = (float)(cg.wonkyTime - cg.time) / 10000.0f; // goes for 10 seconds
1309 
1310 		fov_x += ( 25.0f * perc );
1311 		fov_y -= ( cos( cg.time * 0.0008f ) * 5.0f * perc );
1312 	}
1313 
1314 	// set it
1315 	cg.refdef.fov_x = fov_x;
1316 	cg.refdef.fov_y = fov_y;
1317 
1318 	return (inwater);
1319 }
1320 
CG_ForceSpeedFOV(void)1321 float CG_ForceSpeedFOV( void )
1322 {
1323 	float fov;
1324 	float timeLeft = player->client->ps.forcePowerDuration[FP_SPEED] - cg.time;
1325 	float length = FORCE_SPEED_DURATION*forceSpeedValue[player->client->ps.forcePowerLevel[FP_SPEED]];
1326 	float amt = forceSpeedFOVMod[player->client->ps.forcePowerLevel[FP_SPEED]];
1327 	if ( timeLeft < 500 )
1328 	{//start going back
1329 		fov = cg_fov.value + (timeLeft)/500*amt;
1330 	}
1331 	else if ( length - timeLeft < 1000 )
1332 	{//start zooming in
1333 		fov = cg_fov.value + (length - timeLeft)/1000*amt;
1334 	}
1335 	else
1336 	{//stay at this FOV
1337 		fov = cg_fov.value+amt;
1338 	}
1339 	return fov;
1340 }
1341 /*
1342 ====================
1343 CG_CalcFov
1344 
1345 Fixed fov at intermissions, otherwise account for fov variable and zooms.
1346 ====================
1347 */
CG_CalcFov(void)1348 static qboolean	CG_CalcFov( void ) {
1349 	float	fov_x;
1350 	float	f;
1351 
1352 	if ( cg.predicted_player_state.pm_type == PM_INTERMISSION ) {
1353 		// if in intermission, use a fixed value
1354 		fov_x = 80;
1355 	}
1356 	else if ( cg.snap
1357 		&& cg.snap->ps.viewEntity > 0
1358 		&& cg.snap->ps.viewEntity < ENTITYNUM_WORLD
1359 		&& (!cg.renderingThirdPerson || g_entities[cg.snap->ps.viewEntity].e_DieFunc == dieF_camera_die) )
1360 	{
1361 		// if in entity camera view, use a special FOV
1362 		if ( &g_entities[cg.snap->ps.viewEntity] &&
1363 			g_entities[cg.snap->ps.viewEntity].NPC )
1364 		{//FIXME: looks bad when take over a jedi... but never really do that, do we?
1365 			fov_x = g_entities[cg.snap->ps.viewEntity].NPC->stats.hfov;
1366 			//sanity-cap?
1367 			if ( fov_x > 120 )
1368 			{
1369 				fov_x = 120;
1370 			}
1371 			else if ( fov_x < 10 )
1372 			{
1373 				fov_x = 10;
1374 			}
1375 		}
1376 		else
1377 		{
1378 			if ( cg.overrides.active & CG_OVERRIDE_FOV )
1379 			{
1380 				fov_x = cg.overrides.fov;
1381 			}
1382 			else
1383 			{
1384 				fov_x = 120;//FIXME: read from the NPC's fov stats?
1385 			}
1386 		}
1387 	}
1388 	else if ( (!cg.zoomMode || cg.zoomMode > 2) && (cg.snap->ps.forcePowersActive&(1<<FP_SPEED)) && player->client->ps.forcePowerDuration[FP_SPEED] )//cg.renderingThirdPerson &&
1389 	{
1390 		fov_x = CG_ForceSpeedFOV();
1391 	} else {
1392 		// user selectable
1393 		if ( cg.overrides.active & CG_OVERRIDE_FOV )
1394 		{
1395 			fov_x = cg.overrides.fov;
1396 		}
1397 		else
1398 		{
1399 			fov_x = cg_fov.value;
1400 		}
1401 		if ( fov_x < 1 ) {
1402 			fov_x = 1;
1403 		} else if ( fov_x > 160 ) {
1404 			fov_x = 160;
1405 		}
1406 
1407 		// Disable zooming when in third person
1408 		if ( cg.zoomMode && cg.zoomMode < 3 )//&& !cg.renderingThirdPerson ) // light amp goggles do none of the zoom silliness
1409 		{
1410 			if ( !cg.zoomLocked )
1411 			{
1412 				if ( cg.zoomMode == 1 )
1413 				{
1414 					// binoculars zooming either in or out
1415 					cg_zoomFov += cg.zoomDir * cg.frametime * 0.05f;
1416 				}
1417 				else
1418 				{
1419 					// disruptor zooming in faster
1420 					cg_zoomFov -= cg.frametime * 0.075f;
1421 				}
1422 
1423 				// Clamp zoomFov
1424 				float actualFOV = (cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value;
1425 				if ( cg_zoomFov < MAX_ZOOM_FOV )
1426 				{
1427 					cg_zoomFov = MAX_ZOOM_FOV;
1428 				}
1429 				else if ( cg_zoomFov > actualFOV )
1430 				{
1431 					cg_zoomFov = actualFOV;
1432 				}
1433 				else
1434 				{//still zooming
1435 					static int zoomSoundTime = 0;
1436 
1437 					if ( zoomSoundTime < cg.time )
1438 					{
1439 						sfxHandle_t snd;
1440 
1441 						if ( cg.zoomMode == 1 )
1442 						{
1443 							snd = cgs.media.zoomLoop;
1444 						}
1445 						else
1446 						{
1447 							snd = cgs.media.disruptorZoomLoop;
1448 						}
1449 
1450 						// huh?  This could probably just be added as a looping sound??
1451 						cgi_S_StartSound( cg.refdef.vieworg, ENTITYNUM_WORLD, CHAN_LOCAL, snd );
1452 						zoomSoundTime = cg.time + 150;
1453 					}
1454 				}
1455 			}
1456 
1457 			fov_x = cg_zoomFov;
1458 		} else {
1459 			f = ( cg.time - cg.zoomTime ) / ZOOM_OUT_TIME;
1460 			if ( f <= 1.0 ) {
1461 				fov_x = cg_zoomFov + f * ( fov_x - cg_zoomFov );
1462 			}
1463 		}
1464 	}
1465 
1466 //	g_fov = fov_x;
1467 	return ( CG_CalcFOVFromX( fov_x ) );
1468 }
1469 
1470 
1471 
1472 /*
1473 ===============
1474 CG_DamageBlendBlob
1475 
1476 ===============
1477 */
CG_DamageBlendBlob(void)1478 static void CG_DamageBlendBlob( void )
1479 {
1480 	int			t;
1481 	int			maxTime;
1482 	refEntity_t		ent;
1483 
1484 	if ( !cg.damageValue ) {
1485 		return;
1486 	}
1487 
1488 	maxTime = DAMAGE_TIME;
1489 	t = cg.time - cg.damageTime;
1490 	if ( t <= 0 || t >= maxTime ) {
1491 		return;
1492 	}
1493 
1494 	memset( &ent, 0, sizeof( ent ) );
1495 	ent.reType = RT_SPRITE;
1496 	ent.renderfx = RF_FIRST_PERSON;
1497 
1498 	VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
1499 	VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
1500 	VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
1501 
1502 	ent.radius = cg.damageValue * 3 * ( 1.0 - ((float)t / maxTime) );
1503 	ent.customShader = cgs.media.damageBlendBlobShader;
1504 	ent.shaderRGBA[0] = 180 * ( 1.0 - ((float)t / maxTime) );
1505 	ent.shaderRGBA[1] = 50 * ( 1.0 - ((float)t / maxTime) );
1506 	ent.shaderRGBA[2] = 50 * ( 1.0 - ((float)t / maxTime) );
1507 	ent.shaderRGBA[3] = 255;
1508 
1509 	cgi_R_AddRefEntityToScene( &ent );
1510 }
1511 
1512 /*
1513 ====================
1514 CG_SaberClashFlare
1515 ====================
1516 */
1517 extern int g_saberFlashTime;
1518 extern vec3_t g_saberFlashPos;
1519 extern qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y);
CG_SaberClashFlare(void)1520 void CG_SaberClashFlare( void )
1521 {
1522 	int				t, maxTime = 150;
1523 
1524 	t = cg.time - g_saberFlashTime;
1525 
1526 	if ( t <= 0 || t >= maxTime )
1527 	{
1528 		return;
1529 	}
1530 
1531 	vec3_t dif;
1532 
1533 	// Don't do clashes for things that are behind us
1534 	VectorSubtract( g_saberFlashPos, cg.refdef.vieworg, dif );
1535 
1536 	if ( DotProduct( dif, cg.refdef.viewaxis[0] ) < 0.2 )
1537 	{
1538 		return;
1539 	}
1540 
1541 	trace_t tr;
1542 
1543 	CG_Trace( &tr, cg.refdef.vieworg, NULL, NULL, g_saberFlashPos, -1, CONTENTS_SOLID );
1544 
1545 	if ( tr.fraction < 1.0f )
1546 	{
1547 		return;
1548 	}
1549 
1550 	vec3_t color;
1551 	int x,y;
1552 	float v, len = VectorNormalize( dif );
1553 
1554 	// clamp to a known range
1555 	if ( len > 800 )
1556 	{
1557 		len = 800;
1558 	}
1559 
1560 	v = ( 1.0f - ((float)t / maxTime )) * ((1.0f - ( len / 800.0f )) * 2.0f + 0.35f);
1561 
1562 	CG_WorldCoordToScreenCoord( g_saberFlashPos, &x, &y );
1563 
1564 	VectorSet( color, 0.8f, 0.8f, 0.8f );
1565 	cgi_R_SetColor( color );
1566 
1567 	CG_DrawPic( x - ( v * 300 ), y - ( v * 300 ),
1568 				v * 600, v * 600,
1569 				cgi_R_RegisterShader( "gfx/effects/saberFlare" ));
1570 }
1571 
1572 /*
1573 ===============
1574 CG_CalcViewValues
1575 
1576 Sets cg.refdef view values
1577 ===============
1578 */
CG_CalcViewValues(void)1579 static qboolean CG_CalcViewValues( void ) {
1580 	playerState_t	*ps;
1581 	//qboolean		viewEntIsHumanoid = qfalse;
1582 	qboolean		viewEntIsCam = qfalse;
1583 
1584 	memset( &cg.refdef, 0, sizeof( cg.refdef ) );
1585 
1586 	// calculate size of 3D view
1587 	CG_CalcVrect();
1588 
1589 	if( cg.snap->ps.viewEntity != 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD &&
1590 		g_entities[cg.snap->ps.viewEntity].client)
1591 	{
1592 		ps = &g_entities[cg.snap->ps.viewEntity].client->ps;
1593 		//viewEntIsHumanoid = qtrue;
1594 	}
1595 	else
1596 	{
1597 		ps = &cg.predicted_player_state;
1598 	}
1599 #ifndef FINAL_BUILD
1600 	trap_Com_SetOrgAngles(ps->origin,ps->viewangles);
1601 #endif
1602 	// intermission view
1603 	if ( ps->pm_type == PM_INTERMISSION ) {
1604 		VectorCopy( ps->origin, cg.refdef.vieworg );
1605 		VectorCopy( ps->viewangles, cg.refdefViewAngles );
1606 		AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
1607 		return CG_CalcFov();
1608 	}
1609 
1610 	cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
1611 	cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
1612 	cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
1613 		ps->velocity[1] * ps->velocity[1] );
1614 
1615 	if ( G_IsRidingVehicle( &g_entities[0] ) )
1616 	{
1617 		VectorCopy( ps->origin, cg.refdef.vieworg );
1618 		VectorCopy( cg_entities[g_entities[0].owner->s.number].lerpAngles, cg.refdefViewAngles );
1619 		if ( !(ps->eFlags&EF_NODRAW) )
1620 		{//riding it, not *inside* it
1621 			//let us look up & down
1622 			cg.refdefViewAngles[PITCH] = cg_entities[ps->clientNum].lerpAngles[PITCH] * 0.2f;
1623 		}
1624 	}
1625 	else if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
1626 	{//in an entity camera view
1627 		/*
1628 		if ( g_entities[cg.snap->ps.viewEntity].client && cg.renderingThirdPerson )
1629 		{
1630 			VectorCopy( g_entities[cg.snap->ps.viewEntity].client->renderInfo.eyePoint, cg.refdef.vieworg );
1631 		}
1632 		else
1633 		*/
1634 		{
1635 			VectorCopy( cg_entities[cg.snap->ps.viewEntity].lerpOrigin, cg.refdef.vieworg );
1636 		}
1637 		VectorCopy( cg_entities[cg.snap->ps.viewEntity].lerpAngles, cg.refdefViewAngles );
1638 		if ( !Q_stricmp( "misc_camera", g_entities[cg.snap->ps.viewEntity].classname ) || g_entities[cg.snap->ps.viewEntity].s.weapon == WP_TURRET )
1639 		{
1640 			viewEntIsCam = qtrue;
1641 		}
1642 	}
1643 	else if ( cg.renderingThirdPerson && !cg.zoomMode && (cg.overrides.active&CG_OVERRIDE_3RD_PERSON_ENT) )
1644 	{//different center, same angle
1645 		VectorCopy( cg_entities[cg.overrides.thirdPersonEntity].lerpOrigin, cg.refdef.vieworg );
1646 		VectorCopy( ps->viewangles, cg.refdefViewAngles );
1647 	}
1648 	else
1649 	{//player's center and angles
1650 		VectorCopy( ps->origin, cg.refdef.vieworg );
1651 		VectorCopy( ps->viewangles, cg.refdefViewAngles );
1652 	}
1653 
1654 	// add error decay
1655 	if ( cg_errorDecay.value > 0 ) {
1656 		int		t;
1657 		float	f;
1658 
1659 		t = cg.time - cg.predictedErrorTime;
1660 		f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
1661 		if ( f > 0 && f < 1 ) {
1662 			VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
1663 		} else {
1664 			cg.predictedErrorTime = 0;
1665 		}
1666 	}
1667 
1668 	if ( (cg.renderingThirdPerson||cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE)
1669 		&& !cg.zoomMode
1670 		&& !viewEntIsCam )
1671 	{
1672 		// back away from character
1673 //		if ( cg_thirdPerson.integer == CG_CAM_ABOVE)
1674 //		{			`
1675 //			CG_OffsetThirdPersonOverheadView();
1676 //		}
1677 //		else
1678 //		{
1679 		// First person saber
1680 		if ( !cg.renderingThirdPerson )
1681 		{
1682 			if ( cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE )
1683 			{
1684 				vec3_t dir;
1685 				CG_OffsetFirstPersonView( qtrue );
1686 				cg.refdef.vieworg[2] += 32;
1687 				AngleVectors( cg.refdefViewAngles, dir, NULL, NULL );
1688 				VectorMA( cg.refdef.vieworg, -2, dir, cg.refdef.vieworg );
1689 			}
1690 		}
1691 		CG_OffsetThirdPersonView();
1692 //		}
1693 	}
1694 	else
1695 	{
1696 		// offset for local bobbing and kicks
1697 		CG_OffsetFirstPersonView( qfalse );
1698 		centity_t	*playerCent = &cg_entities[0];
1699 		if ( playerCent && playerCent->gent && playerCent->gent->client )
1700 		{
1701 			VectorCopy( cg.refdef.vieworg, playerCent->gent->client->renderInfo.eyePoint );
1702 			VectorCopy( cg.refdefViewAngles, playerCent->gent->client->renderInfo.eyeAngles );
1703 			if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
1704 			{//in an entity camera view
1705 				if ( cg_entities[cg.snap->ps.viewEntity].gent->client )
1706 				{//looking through a client's eyes
1707 					VectorCopy( cg.refdef.vieworg, cg_entities[cg.snap->ps.viewEntity].gent->client->renderInfo.eyePoint );
1708 					VectorCopy( cg.refdefViewAngles, cg_entities[cg.snap->ps.viewEntity].gent->client->renderInfo.eyeAngles );
1709 				}
1710 				else
1711 				{//looking through a regular ent's eyes
1712 					VectorCopy( cg.refdef.vieworg, cg_entities[cg.snap->ps.viewEntity].lerpOrigin );
1713 					VectorCopy( cg.refdefViewAngles, cg_entities[cg.snap->ps.viewEntity].lerpAngles );
1714 				}
1715 			}
1716 			VectorCopy( playerCent->gent->client->renderInfo.eyePoint, playerCent->gent->client->renderInfo.headPoint );
1717 			if ( cg.snap->ps.viewEntity <= 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD )
1718 			{//not in entity cam
1719 				playerCent->gent->client->renderInfo.headPoint[2] -= 8;
1720 			}
1721 		}
1722 	}
1723 
1724 	//VectorCopy( cg.refdef.vieworg, cgRefdefVieworg );
1725 	// shake the camera if necessary
1726 	CGCam_UpdateSmooth( cg.refdef.vieworg, cg.refdefViewAngles );
1727 	CGCam_UpdateShake( cg.refdef.vieworg, cg.refdefViewAngles );
1728 
1729 	/*
1730 	if ( in_camera )
1731 	{
1732 		Com_Printf( "%s %s\n", vtos(client_camera.origin), vtos(cg.refdef.vieworg) );
1733 	}
1734 	*/
1735 
1736 	// see if we are drugged by an interrogator.  We muck with the angles here, just a bit earlier, we mucked with the FOV
1737 	if ( cg.wonkyTime > 0 && cg.wonkyTime > cg.time )
1738 	{
1739 		float perc = (float)(cg.wonkyTime - cg.time) / 10000.0f; // goes for 10 seconds
1740 
1741 		cg.refdefViewAngles[ROLL] += ( sin( cg.time * 0.0004f )  * 7.0f * perc );
1742 		cg.refdefViewAngles[PITCH] += ( 26.0f * perc + sin( cg.time * 0.0011f ) * 3.0f * perc );
1743 	}
1744 
1745 	AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
1746 
1747 	if ( cg.hyperspace )
1748 	{
1749 		cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
1750 	}
1751 
1752 	// field of view
1753 	return CG_CalcFov();
1754 }
1755 
1756 
1757 /*
1758 =====================
1759 CG_PowerupTimerSounds
1760 =====================
1761 */
CG_PowerupTimerSounds(void)1762 static void CG_PowerupTimerSounds( void )
1763 {
1764 	int	i, time;
1765 
1766 	// powerup timers going away
1767 	for ( i = 0 ; i < MAX_POWERUPS ; i++ )
1768 	{
1769 		time = cg.snap->ps.powerups[i];
1770 
1771 		if ( time > 0 && time < cg.time )
1772 		{
1773 			// add any special powerup expiration sounds here
1774 //			switch( i )
1775 //			{
1776 //			case PW_WEAPON_OVERCHARGE:
1777 //				cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.overchargeEndSound );
1778 //				break;
1779 //			}
1780 		}
1781 	}
1782 }
1783 
1784 /*
1785 ==============
1786 CG_DrawSkyBoxPortal
1787 ==============
1788 */
1789 extern void cgi_CM_SnapPVS(vec3_t origin,byte *buffer);
1790 
CG_DrawSkyBoxPortal(void)1791 static void CG_DrawSkyBoxPortal(void)
1792 {
1793 	refdef_t backuprefdef;
1794 	const char *cstr;
1795 	char *token;
1796 
1797 	cstr = CG_ConfigString(CS_SKYBOXORG);
1798 
1799 	if (!cstr || !strlen(cstr))
1800 	{
1801 		// no skybox in this map
1802 		return;
1803 	}
1804 
1805 	backuprefdef = cg.refdef;
1806 
1807 	// asdf --eez
1808 	COM_BeginParseSession();
1809 	token = COM_ParseExt(&cstr, qfalse);
1810 	if (!token || !token[0])
1811 	{
1812 		CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
1813 	}
1814 	cg.refdef.vieworg[0] = atof(token);
1815 
1816 	token = COM_ParseExt(&cstr, qfalse);
1817 	if (!token || !token[0])
1818 	{
1819 		CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
1820 	}
1821 	cg.refdef.vieworg[1] = atof(token);
1822 
1823 	token = COM_ParseExt(&cstr, qfalse);
1824 	if (!token || !token[0])
1825 	{
1826 		CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
1827 	}
1828 	cg.refdef.vieworg[2] = atof(token);
1829 
1830 	// setup fog the first time, ignore this part of the configstring after that
1831 	token = COM_ParseExt(&cstr, qfalse);
1832 	if (!token || !token[0])
1833 	{
1834 		CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog state\n");
1835 	}
1836 	else
1837 	{
1838 		if ( atoi( token ) ) {
1839 			// this camera has fog
1840 			token = COM_ParseExt( &cstr, qfalse );
1841 			if ( !VALIDSTRING( token ) ) {
1842 				CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog[0]\n");
1843 			}
1844 
1845 			token = COM_ParseExt(&cstr, qfalse);
1846 			if ( !VALIDSTRING( token ) ) {
1847 				CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog[1]\n");
1848 			}
1849 
1850 			token = COM_ParseExt(&cstr, qfalse);
1851 			if ( !VALIDSTRING( token ) ) {
1852 				CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog[2]\n");
1853 			}
1854 
1855 			COM_ParseExt( &cstr, qfalse );
1856 			COM_ParseExt( &cstr, qfalse );
1857 		}
1858 	}
1859 
1860 	COM_EndParseSession();
1861 /*
1862 	static float lastfov = cg_zoomFov;	// for transitions back from zoomed in modes
1863 	float fov_x;
1864 	float fov_y;
1865 	float x;
1866 	float f = 0;
1867 
1868 	if ( cg.predicted_player_state.pm_type == PM_INTERMISSION )
1869 	{
1870 		// if in intermission, use a fixed value
1871 		fov_x = (cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value;
1872 	}
1873 	else
1874 	{
1875 		if (cg.zoomMode)
1876 		{
1877 			fov_x = cg_zoomFov;
1878 		}
1879 		else
1880 		{
1881 			fov_x = (cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value;
1882 			if ( fov_x < 1 )
1883 			{
1884 				fov_x = 1;
1885 			}
1886 			else if ( fov_x > 160 )
1887 			{
1888 				fov_x = 160;
1889 			}
1890 		}
1891 
1892 		// do smooth transitions for zooming
1893 		if (cg.zoomMode)
1894 		{ //zoomed/zooming in
1895 			f = ( cg.time - cg.zoomTime ) / (float)ZOOM_OUT_TIME;
1896 			if ( f > 1.0 ) {
1897 				fov_x = cg_zoomFov;
1898 			} else {
1899 				fov_x = fov_x + f * ( cg_zoomFov - fov_x );
1900 			}
1901 			lastfov = fov_x;
1902 		}
1903 		else
1904 		{ //zooming out
1905 			f = ( cg.time - cg.zoomTime ) / (float)ZOOM_OUT_TIME;
1906 			if ( f > 1.0 ) {
1907 				fov_x = fov_x;
1908 			} else {
1909 				fov_x = cg_zoomFov + f * ( fov_x - cg_zoomFov);
1910 			}
1911 		}
1912 	}
1913 
1914 	x = cg.refdef.width / tan( fov_x / 360 * M_PI );
1915 	fov_y = atan2( cg.refdef.height, x );
1916 	fov_y = fov_y * 360 / M_PI;
1917 
1918 	cg.refdef.fov_x = fov_x;
1919 	cg.refdef.fov_y = fov_y;
1920 */
1921 	//inherit fov and axis from whatever the player is doing (regular, camera overrides or zoomed, whatever)
1922 	if ( !cg.hyperspace )
1923 	{
1924 		CG_AddPacketEntities(qtrue); //rww - There was no proper way to put real entities inside the portal view before.
1925 									 //This will put specially flagged entities in the render.
1926 		//Add effects flagged to play only in portals
1927 		theFxScheduler.AddScheduledEffects( true );
1928 	}
1929 
1930 	cg.refdef.rdflags |= RDF_SKYBOXPORTAL;	//mark portal scene specialness
1931 	cg.refdef.rdflags |= RDF_DRAWSKYBOX;	//drawk portal skies
1932 
1933 	cgi_CM_SnapPVS( cg.refdef.vieworg, cg.refdef.areamask );	//fill in my areamask for this view origin
1934 	// draw the skybox
1935 	cgi_R_RenderScene( &cg.refdef );
1936 
1937 	cg.refdef = backuprefdef;
1938 }
1939 
1940 //----------------------------
CG_RunEmplacedWeapon()1941 void CG_RunEmplacedWeapon()
1942 {
1943 	gentity_t	*player = &g_entities[0],
1944 				*gun = player->owner;
1945 
1946 	// Override the camera when we are locked onto the gun.
1947 	if ( player
1948 		&& gun
1949 		&& !gun->bounceCount//not an eweb
1950 		&& ( player->s.eFlags & EF_LOCKED_TO_WEAPON ))
1951 	{
1952 //		float dist = -1; // default distance behind gun
1953 
1954 		// don't let the player try and change this
1955 		cg.renderingThirdPerson = qtrue;
1956 
1957 //		cg.refdefViewAngles[PITCH] += cg.overrides.thirdPersonPitchOffset? cg.overrides.thirdPersonPitchOffset: cg_thirdPersonPitchOffset.value;
1958 //		cg.refdefViewAngles[YAW] += cg.overrides.thirdPersonAngle ? cg.overrides.thirdPersonAngle : cg_thirdPersonAngle.value;;
1959 
1960 		AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
1961 
1962 		// Slide in behind the gun.
1963 //		if ( gun->delay + 500 > cg.time )
1964 //		{
1965 //			dist -= (( gun->delay + 500 ) - cg.time ) * 0.02f;
1966 //		}
1967 
1968 		VectorCopy( gun->pos2, cg.refdef.vieworg );
1969 		VectorMA( cg.refdef.vieworg, -20.0f, gun->pos3, cg.refdef.vieworg );
1970 		if ( cg.snap->ps.viewEntity <= 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD )
1971 		{
1972 			VectorMA( cg.refdef.vieworg, 35.0f, gun->pos4, cg.refdef.vieworg );
1973 		}
1974 
1975 	}
1976 }
1977 
1978 //=========================================================================
1979 
1980 /*
1981 =================
1982 CG_DrawActiveFrame
1983 
1984 Generates and draws a game scene and status information at the given time.
1985 =================
1986 */
1987 extern void CG_BuildSolidList( void );
1988 extern void CG_ClearHealthBarEnts( void );
1989 extern vec3_t	serverViewOrg;
1990 static qboolean cg_rangedFogging = qfalse; //so we know if we should go back to normal fog
CG_DrawActiveFrame(int serverTime,stereoFrame_t stereoView)1991 void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) {
1992 	qboolean	inwater = qfalse;
1993 
1994 	cg.time = serverTime;
1995 
1996 	// update cvars
1997 	CG_UpdateCvars();
1998 
1999 	// if we are only updating the screen as a loading
2000 	// pacifier, don't even try to read snapshots
2001 	if ( cg.infoScreenText[0] != 0 ) {
2002 		CG_DrawInformation();
2003 		return;
2004 	}
2005 
2006 	CG_ClearHealthBarEnts();
2007 
2008 	CG_RunLightStyles();
2009 
2010 	// any looped sounds will be respecified as entities
2011 	// are added to the render list
2012 	cgi_S_ClearLoopingSounds();
2013 
2014 	// clear all the render lists
2015 	cgi_R_ClearScene();
2016 
2017 	CG_BuildSolidList();
2018 
2019 	// set up cg.snap and possibly cg.nextSnap
2020 	CG_ProcessSnapshots();
2021 	// if we haven't received any snapshots yet, all
2022 	// we can draw is the information screen
2023 	if ( !cg.snap ) {
2024 		//CG_DrawInformation();
2025 		return;
2026 	}
2027 
2028 	// make sure the lagometerSample and frame timing isn't done twice when in stereo
2029 	if ( stereoView != STEREO_RIGHT ) {
2030 		cg.frametime = cg.time - cg.oldTime;
2031 		cg.oldTime = cg.time;
2032 	}
2033 	// Make sure the helper has the updated time
2034 	theFxHelper.AdjustTime( cg.frametime );
2035 
2036 	// let the client system know what our weapon and zoom settings are
2037 	//FIXME: should really send forcePowersActive over network onto cg.snap->ps...
2038 	const int fpActive = cg_entities[0].gent->client->ps.forcePowersActive;
2039 	const bool matrixMode = !!(fpActive & ((1 << FP_SPEED) | (1 << FP_RAGE)));
2040 	float speed = cg.refdef.fov_y / 75.0 * (matrixMode ? 1.0f : cg_timescale.value);
2041 
2042 //FIXME: junk code, BUG:168
2043 
2044 	static bool wasForceSpeed=false;
2045 	bool isForceSpeed=cg_entities[0].gent->client->ps.forcePowersActive&(1<<FP_SPEED)?true:false;
2046 	if (isForceSpeed&&!wasForceSpeed)
2047 	{
2048 		CGCam_Smooth(0.75f,5000);
2049 	}
2050 	wasForceSpeed=isForceSpeed;
2051 
2052 //
2053 
2054 	float mPitchOverride = 0.0f;
2055 	float mYawOverride = 0.0f;
2056 	if ( cg.snap->ps.clientNum == 0 && cg_scaleVehicleSensitivity.integer )
2057 	{//pointless check, but..
2058 		if ( cg_entities[0].gent->s.eFlags & EF_LOCKED_TO_WEAPON )
2059 		{
2060 			speed *= 0.25f;
2061 		}
2062 		Vehicle_t *pVeh = NULL;
2063 
2064 		// Mouse turns slower.
2065 		if ( ( pVeh = G_IsRidingVehicle( &g_entities[0] ) ) != NULL )
2066 		{
2067 			if ( pVeh->m_pVehicleInfo->mousePitch )
2068 			{
2069 				mPitchOverride = pVeh->m_pVehicleInfo->mousePitch;
2070 			}
2071 			if ( pVeh->m_pVehicleInfo->mouseYaw )
2072 			{
2073 				mYawOverride = pVeh->m_pVehicleInfo->mouseYaw;
2074 			}
2075 		}
2076 	}
2077 	cgi_SetUserCmdValue( cg.weaponSelect, speed, mPitchOverride, mYawOverride );
2078 
2079 	// this counter will be bumped for every valid scene we generate
2080 	cg.clientFrame++;
2081 
2082 	// update cg.predicted_player_state
2083 	CG_PredictPlayerState();
2084 
2085 	if (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
2086 	{
2087 		cg.zoomMode = 0;
2088 	}
2089 	// decide on third person view
2090 	cg.renderingThirdPerson = (qboolean)(
2091 		cg_thirdPerson.integer
2092 		|| (cg.snap->ps.stats[STAT_HEALTH] <= 0)
2093 		|| (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
2094 		|| ((g_entities[0].client&&g_entities[0].client->NPC_class==CLASS_ATST)
2095 		|| (cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE) ));
2096 
2097 	if ( cg.zoomMode )
2098 	{
2099 		// zoomed characters should never do third person stuff??
2100 		cg.renderingThirdPerson = qfalse;
2101 	}
2102 
2103 	if ( in_camera )
2104 	{
2105 		// The camera takes over the view
2106 		CGCam_RenderScene();
2107 	}
2108 	else
2109 	{
2110 		//Finish any fading that was happening
2111 		CGCam_UpdateFade();
2112 		// build cg.refdef
2113 		inwater = CG_CalcViewValues();
2114 	}
2115 
2116 	if (cg.zoomMode)
2117 	{ //zooming with binoculars or sniper, set the fog range based on the zoom level -rww
2118 		cg_rangedFogging = qtrue;
2119 		//smaller the fov the less fog we have between the view and cull dist
2120 		cgi_R_SetRangeFog(cg.refdef.fov_x*64.0f);
2121 	}
2122 	else if (cg_rangedFogging)
2123 	{ //disable it
2124 		cg_rangedFogging = qfalse;
2125 		cgi_R_SetRangeFog(0.0f);
2126 	}
2127 
2128 	cg.refdef.time = cg.time;
2129 
2130 	CG_DrawSkyBoxPortal();
2131 
2132 	// NOTE: this may completely override the camera
2133 	CG_RunEmplacedWeapon();
2134 
2135 	// first person blend blobs, done after AnglesToAxis
2136 	if ( !cg.renderingThirdPerson ) {
2137 		CG_DamageBlendBlob();
2138 	}
2139 
2140 	// build the render lists
2141 	if ( !cg.hyperspace ) {
2142 		CG_AddPacketEntities(qfalse);			// adter calcViewValues, so predicted player state is correct
2143 		CG_AddMarks();
2144 		CG_AddLocalEntities();
2145 		CG_DrawMiscEnts();
2146 	}
2147 
2148 	//check for opaque water
2149 	// this game does not have opaque water
2150 	if ( 0 )
2151 	{
2152 		vec3_t	camTest;
2153 		VectorCopy( cg.refdef.vieworg, camTest );
2154 		camTest[2] += 6;
2155 		if ( !(CG_PointContents( camTest, 0 )&CONTENTS_SOLID) && !gi.inPVS( cg.refdef.vieworg, camTest ) )
2156 		{//crossed visible line into another room
2157 			cg.refdef.vieworg[2] -= 6;
2158 			//cgi_CM_SnapPVS(cg.refdef.vieworg,cg.snap->areamask);
2159 		}
2160 		else
2161 		{
2162 			VectorCopy( cg.refdef.vieworg, camTest );
2163 			camTest[2] -= 6;
2164 			if ( !(CG_PointContents( camTest, 0 )&CONTENTS_SOLID) && !gi.inPVS( cg.refdef.vieworg, camTest ) )
2165 			{
2166 				cg.refdef.vieworg[2] += 6;
2167 				//cgi_CM_SnapPVS(cg.refdef.vieworg,cg.snap->areamask);
2168 			}
2169 			else //if ( inwater )
2170 			{//extra-special hack... sometimes when crouched in water with first person lightsaber, your PVS is wrong???
2171 				/*
2172 				if ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) )
2173 				{//pseudo first-person for saber and fists
2174 					cgi_CM_SnapPVS(cg.refdef.vieworg,cg.snap->areamask);
2175 				}
2176 				*/
2177 			}
2178 		}
2179 	}
2180 	//FIXME: first person crouch-uncrouch STILL FUCKS UP THE AREAMASK!!!
2181 	//if ( !VectorCompare2( cg.refdef.vieworg, cg.snap->ps.serverViewOrg ) && !gi.inPVS( cg.refdef.vieworg, cg.snap->ps.serverViewOrg ) )
2182 	{//actual view org and server's view org don't match and aren't same PVS, rebuild the areamask
2183 		//Com_Printf( S_COLOR_RED"%s != %s\n", vtos(cg.refdef.vieworg), vtos(cg.snap->ps.serverViewOrg) );
2184 		cgi_CM_SnapPVS( cg.refdef.vieworg, cg.snap->areamask );
2185 	}
2186 
2187 	// Don't draw the in-view weapon when in camera mode
2188 	if ( !in_camera
2189 		&& !cg_pano.integer
2190 		&& cg.snap->ps.weapon != WP_SABER
2191 		&& ( cg.snap->ps.viewEntity == 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD ) )
2192 	{
2193 		CG_AddViewWeapon( &cg.predicted_player_state );
2194 	}
2195 	else if( cg.snap->ps.viewEntity != 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
2196 	{
2197 		if( g_entities[cg.snap->ps.viewEntity].client && g_entities[cg.snap->ps.viewEntity].NPC )
2198 		{
2199 			CG_AddViewWeapon( &g_entities[cg.snap->ps.viewEntity ].client->ps );	// HAX - because I wanted to --eez
2200 		}
2201 	}
2202 
2203 	if ( !cg.hyperspace && fx_freeze.integer<2 )
2204 	{
2205 		//Add all effects
2206 		theFxScheduler.AddScheduledEffects( false );
2207 	}
2208 
2209 	// finish up the rest of the refdef
2210 	if ( cg.testModelEntity.hModel ) {
2211 		CG_AddTestModel();
2212 	}
2213 
2214 	memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
2215 
2216 	// update audio positions
2217 	//This is done from the vieworg to get origin for non-attenuated sounds
2218 	cgi_S_UpdateAmbientSet( CG_ConfigString( CS_AMBIENT_SET ), cg.refdef.vieworg );
2219 	//NOTE: if we want to make you be able to hear far away sounds with electrobinoculars, add the hacked-in positional offset here (base on fov)
2220 	/*
2221 	vec3_t listener_origin;
2222 	VectorCopy( cg.refdef.vieworg, listener_origin );
2223 	if ( cg.snap->ps.forcePowersActive&(1<<FP_SEE)
2224 		&& cg.snap->ps.forcePowerLevel[FP_SEE] >= FORCE_LEVEL_2 )
2225 	{//FIXME: if I can see through walls, why not actually move the listener_position through the wall too?
2226 		if ( g_crosshairEntNum < ENTITYNUM_WORLD
2227 			&& g_crosshairSameEntTime >= 1000 )
2228 		{//been looking at this guy for at least a full second
2229 			gentity_t *crossEnt = &g_entities[g_crosshairEntNum];
2230 			if ( crossEnt->client && crossEnt->health >= 0 )
2231 			{//a living client, let's listen in...?
2232 				//FIXME: lerp our listener_origin to their position over a second or two?
2233 				VectorCopy( cg_entities[g_crosshairEntNum].lerpOrigin, listener_origin );
2234 				inwater = qtrue;//do cool reverby effect...?
2235 			}
2236 		}
2237 	}
2238 	cgi_S_Respatialize( cg.snap->ps.clientNum, listener_origin, cg.refdef.viewaxis, inwater );
2239 	*/
2240 	cgi_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
2241 
2242 	// warning sounds when powerup is wearing off
2243 	CG_PowerupTimerSounds();
2244 
2245 	if ( cg_pano.integer ) {	// let's grab a panorama!
2246 		cg.levelShot = qtrue;  //hide the 2d
2247 		VectorClear(cg.refdefViewAngles);
2248 		cg.refdefViewAngles[YAW] = -360 * cg_pano.integer/cg_panoNumShots.integer;	//choose angle
2249 		AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
2250 		CG_DrawActive( stereoView );
2251 		cg.levelShot = qfalse;
2252 	} 	else {
2253 		// actually issue the rendering calls
2254 		CG_DrawActive( stereoView );
2255 	}
2256 	/*
2257 	if ( in_camera && !cg_skippingcin.integer )
2258 	{
2259 		Com_Printf( S_COLOR_GREEN"ang: %s\n", vtos(cg.refdefViewAngles) );
2260 	}
2261 	*/
2262 }
2263 
2264