1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 //
23 // cg_view.c -- setup all the parameters (position, angle, etc)
24 // for a 3D rendering
25 #include "cg_local.h"
26
27
28 /*
29 =============================================================================
30
31 MODEL TESTING
32
33 The viewthing and gun positioning tools from Q2 have been integrated and
34 enhanced into a single model testing facility.
35
36 Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
37
38 The names must be the full pathname after the basedir, like
39 "models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
40
41 Testmodel will create a fake entity 100 units in front of the current view
42 position, directly facing the viewer. It will remain immobile, so you can
43 move around it to view it from different angles.
44
45 Testgun will cause the model to follow the player around and supress the real
46 view weapon model. The default frame 0 of most guns is completely off screen,
47 so you will probably have to cycle a couple frames to see it.
48
49 "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
50 frame or skin of the testmodel. These are bound to F5, F6, F7, and F8 in
51 q3default.cfg.
52
53 If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
54 you adjust the positioning.
55
56 Note that none of the model testing features update while the game is paused, so
57 it may be convenient to test with deathmatch set to 1 so that bringing down the
58 console doesn't pause the game.
59
60 =============================================================================
61 */
62
63 /*
64 =================
65 CG_TestModel_f
66
67 Creates an entity in front of the current position, which
68 can then be moved around
69 =================
70 */
CG_TestModel_f(void)71 void CG_TestModel_f (void) {
72 vec3_t angles;
73
74 memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
75 if ( trap_Argc() < 2 ) {
76 return;
77 }
78
79 Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
80 cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
81
82 if ( trap_Argc() == 3 ) {
83 cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
84 cg.testModelEntity.frame = 1;
85 cg.testModelEntity.oldframe = 0;
86 }
87 if (! cg.testModelEntity.hModel ) {
88 CG_Printf( "Can't register model\n" );
89 return;
90 }
91
92 VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
93
94 angles[PITCH] = 0;
95 angles[YAW] = 180 + cg.refdefViewAngles[1];
96 angles[ROLL] = 0;
97
98 AnglesToAxis( angles, cg.testModelEntity.axis );
99 cg.testGun = qfalse;
100 }
101
102 /*
103 =================
104 CG_TestGun_f
105
106 Replaces the current view weapon with the given model
107 =================
108 */
CG_TestGun_f(void)109 void CG_TestGun_f (void) {
110 CG_TestModel_f();
111 cg.testGun = qtrue;
112 cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON;
113 }
114
115
CG_TestModelNextFrame_f(void)116 void CG_TestModelNextFrame_f (void) {
117 cg.testModelEntity.frame++;
118 CG_Printf( "frame %i\n", cg.testModelEntity.frame );
119 }
120
CG_TestModelPrevFrame_f(void)121 void CG_TestModelPrevFrame_f (void) {
122 cg.testModelEntity.frame--;
123 if ( cg.testModelEntity.frame < 0 ) {
124 cg.testModelEntity.frame = 0;
125 }
126 CG_Printf( "frame %i\n", cg.testModelEntity.frame );
127 }
128
CG_TestModelNextSkin_f(void)129 void CG_TestModelNextSkin_f (void) {
130 cg.testModelEntity.skinNum++;
131 CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
132 }
133
CG_TestModelPrevSkin_f(void)134 void CG_TestModelPrevSkin_f (void) {
135 cg.testModelEntity.skinNum--;
136 if ( cg.testModelEntity.skinNum < 0 ) {
137 cg.testModelEntity.skinNum = 0;
138 }
139 CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
140 }
141
CG_AddTestModel(void)142 static void CG_AddTestModel (void) {
143 int i;
144
145 // re-register the model, because the level may have changed
146 cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
147 if (! cg.testModelEntity.hModel ) {
148 CG_Printf ("Can't register model\n");
149 return;
150 }
151
152 // if testing a gun, set the origin reletive to the view origin
153 if ( cg.testGun ) {
154 VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
155 VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] );
156 VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] );
157 VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] );
158
159 // allow the position to be adjusted
160 for (i=0 ; i<3 ; i++) {
161 cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value;
162 cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value;
163 cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value;
164 }
165 }
166
167 trap_R_AddRefEntityToScene( &cg.testModelEntity );
168 }
169
170
171
172 //============================================================================
173
174
175 /*
176 =================
177 CG_CalcVrect
178
179 Sets the coordinates of the rendered window
180 =================
181 */
CG_CalcVrect(void)182 static void CG_CalcVrect (void) {
183 int size;
184
185 // the intermission should allways be full screen
186 if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
187 size = 100;
188 } else {
189 // bound normal viewsize
190 if (cg_viewsize.integer < 30) {
191 trap_Cvar_Set ("cg_viewsize","30");
192 size = 30;
193 } else if (cg_viewsize.integer > 100) {
194 trap_Cvar_Set ("cg_viewsize","100");
195 size = 100;
196 } else {
197 size = cg_viewsize.integer;
198 }
199
200 }
201 cg.refdef.width = cgs.glconfig.vidWidth*size/100;
202 cg.refdef.width &= ~1;
203
204 cg.refdef.height = cgs.glconfig.vidHeight*size/100;
205 cg.refdef.height &= ~1;
206
207 cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2;
208 cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2;
209 }
210
211 //==============================================================================
212
213
214 /*
215 ===============
216 CG_OffsetThirdPersonView
217
218 ===============
219 */
220 #define FOCUS_DISTANCE 512
CG_OffsetThirdPersonView(void)221 static void CG_OffsetThirdPersonView( void ) {
222 vec3_t forward, right, up;
223 vec3_t view;
224 vec3_t focusAngles;
225 trace_t trace;
226 static vec3_t mins = { -4, -4, -4 };
227 static vec3_t maxs = { 4, 4, 4 };
228 vec3_t focusPoint;
229 float focusDist;
230 float forwardScale, sideScale;
231
232 cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;
233
234 VectorCopy( cg.refdefViewAngles, focusAngles );
235
236 // if dead, look at killer
237 if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
238 focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
239 cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
240 }
241
242 if ( focusAngles[PITCH] > 45 ) {
243 focusAngles[PITCH] = 45; // don't go too far overhead
244 }
245 AngleVectors( focusAngles, forward, NULL, NULL );
246
247 VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
248
249 VectorCopy( cg.refdef.vieworg, view );
250
251 view[2] += 8;
252
253 cg.refdefViewAngles[PITCH] *= 0.5;
254
255 AngleVectors( cg.refdefViewAngles, forward, right, up );
256
257 forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
258 sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
259 VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
260 VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
261
262 // trace a ray from the origin to the viewpoint to make sure the view isn't
263 // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
264
265 if (!cg_cameraMode.integer) {
266 CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
267
268 if ( trace.fraction != 1.0 ) {
269 VectorCopy( trace.endpos, view );
270 view[2] += (1.0 - trace.fraction) * 32;
271 // try another trace to this position, because a tunnel may have the ceiling
272 // close enogh that this is poking out
273
274 CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
275 VectorCopy( trace.endpos, view );
276 }
277 }
278
279
280 VectorCopy( view, cg.refdef.vieworg );
281
282 // select pitch to look at focus point from vieword
283 VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
284 focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
285 if ( focusDist < 1 ) {
286 focusDist = 1; // should never happen
287 }
288 cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
289 cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
290 }
291
292
293 // this causes a compiler bug on mac MrC compiler
CG_StepOffset(void)294 static void CG_StepOffset( void ) {
295 int timeDelta;
296
297 // smooth out stair climbing
298 timeDelta = cg.time - cg.stepTime;
299 if ( timeDelta < STEP_TIME ) {
300 cg.refdef.vieworg[2] -= cg.stepChange
301 * (STEP_TIME - timeDelta) / STEP_TIME;
302 }
303 }
304
305 /*
306 ===============
307 CG_OffsetFirstPersonView
308
309 ===============
310 */
CG_OffsetFirstPersonView(void)311 static void CG_OffsetFirstPersonView( void ) {
312 float *origin;
313 float *angles;
314 float bob;
315 float ratio;
316 float delta;
317 float speed;
318 float f;
319 vec3_t predictedVelocity;
320 int timeDelta;
321
322 if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
323 return;
324 }
325
326 origin = cg.refdef.vieworg;
327 angles = cg.refdefViewAngles;
328
329 // if dead, fix the angle and don't add any kick
330 if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
331 angles[ROLL] = 40;
332 angles[PITCH] = -15;
333 angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
334 origin[2] += cg.predictedPlayerState.viewheight;
335 return;
336 }
337
338 // add angles based on weapon kick
339 VectorAdd (angles, cg.kick_angles, angles);
340
341 // add angles based on damage kick
342 if ( cg.damageTime ) {
343 ratio = cg.time - cg.damageTime;
344 if ( ratio < DAMAGE_DEFLECT_TIME ) {
345 ratio /= DAMAGE_DEFLECT_TIME;
346 angles[PITCH] += ratio * cg.v_dmg_pitch;
347 angles[ROLL] += ratio * cg.v_dmg_roll;
348 } else {
349 ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
350 if ( ratio > 0 ) {
351 angles[PITCH] += ratio * cg.v_dmg_pitch;
352 angles[ROLL] += ratio * cg.v_dmg_roll;
353 }
354 }
355 }
356
357 // add pitch based on fall kick
358 #if 0
359 ratio = ( cg.time - cg.landTime) / FALL_TIME;
360 if (ratio < 0)
361 ratio = 0;
362 angles[PITCH] += ratio * cg.fall_value;
363 #endif
364
365 // add angles based on velocity
366 VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );
367
368 delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
369 angles[PITCH] += delta * cg_runpitch.value;
370
371 delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
372 angles[ROLL] -= delta * cg_runroll.value;
373
374 // add angles based on bob
375
376 // make sure the bob is visible even at low speeds
377 speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
378
379 delta = cg.bobfracsin * cg_bobpitch.value * speed;
380 if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
381 delta *= 3; // crouching
382 angles[PITCH] += delta;
383 delta = cg.bobfracsin * cg_bobroll.value * speed;
384 if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
385 delta *= 3; // crouching accentuates roll
386 if (cg.bobcycle & 1)
387 delta = -delta;
388 angles[ROLL] += delta;
389
390 //===================================
391
392 // add view height
393 origin[2] += cg.predictedPlayerState.viewheight;
394
395 // smooth out duck height changes
396 timeDelta = cg.time - cg.duckTime;
397 if ( timeDelta < DUCK_TIME) {
398 cg.refdef.vieworg[2] -= cg.duckChange
399 * (DUCK_TIME - timeDelta) / DUCK_TIME;
400 }
401
402 // add bob height
403 bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
404 if (bob > 6) {
405 bob = 6;
406 }
407
408 origin[2] += bob;
409
410
411 // add fall height
412 delta = cg.time - cg.landTime;
413 if ( delta < LAND_DEFLECT_TIME ) {
414 f = delta / LAND_DEFLECT_TIME;
415 cg.refdef.vieworg[2] += cg.landChange * f;
416 } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
417 delta -= LAND_DEFLECT_TIME;
418 f = 1.0 - ( delta / LAND_RETURN_TIME );
419 cg.refdef.vieworg[2] += cg.landChange * f;
420 }
421
422 // add step offset
423 CG_StepOffset();
424
425 // add kick offset
426
427 VectorAdd (origin, cg.kick_origin, origin);
428
429 // pivot the eye based on a neck length
430 #if 0
431 {
432 #define NECK_LENGTH 8
433 vec3_t forward, up;
434
435 cg.refdef.vieworg[2] -= NECK_LENGTH;
436 AngleVectors( cg.refdefViewAngles, forward, NULL, up );
437 VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
438 VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
439 }
440 #endif
441 }
442
443 //======================================================================
444
CG_ZoomDown_f(void)445 void CG_ZoomDown_f( void ) {
446 if ( cg.zoomed ) {
447 return;
448 }
449 cg.zoomed = qtrue;
450 cg.zoomTime = cg.time;
451 }
452
CG_ZoomUp_f(void)453 void CG_ZoomUp_f( void ) {
454 if ( !cg.zoomed ) {
455 return;
456 }
457 cg.zoomed = qfalse;
458 cg.zoomTime = cg.time;
459 }
460
461
462 /*
463 ====================
464 CG_CalcFov
465
466 Fixed fov at intermissions, otherwise account for fov variable and zooms.
467 ====================
468 */
469 #define WAVE_AMPLITUDE 1
470 #define WAVE_FREQUENCY 0.4
471
CG_CalcFov(void)472 static int CG_CalcFov( void ) {
473 float x;
474 float phase;
475 float v;
476 int contents;
477 float fov_x, fov_y;
478 float zoomFov;
479 float f;
480 int inwater;
481
482 if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
483 // if in intermission, use a fixed value
484 fov_x = 90;
485 } else {
486 // user selectable
487 if ( cgs.dmflags & DF_FIXED_FOV ) {
488 // dmflag to prevent wide fov for all clients
489 fov_x = 90;
490 } else {
491 fov_x = cg_fov.value;
492 if ( fov_x < 1 ) {
493 fov_x = 1;
494 } else if ( fov_x > 160 ) {
495 fov_x = 160;
496 }
497 }
498
499 // account for zooms
500 zoomFov = cg_zoomFov.value;
501 if ( zoomFov < 1 ) {
502 zoomFov = 1;
503 } else if ( zoomFov > 160 ) {
504 zoomFov = 160;
505 }
506
507 if ( cg.zoomed ) {
508 f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
509 if ( f > 1.0 ) {
510 fov_x = zoomFov;
511 } else {
512 fov_x = fov_x + f * ( zoomFov - fov_x );
513 }
514 } else {
515 f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
516 if ( f > 1.0 ) {
517 fov_x = fov_x;
518 } else {
519 fov_x = zoomFov + f * ( fov_x - zoomFov );
520 }
521 }
522 }
523
524 x = cg.refdef.width / tan( fov_x / 360 * M_PI );
525 fov_y = atan2( cg.refdef.height, x );
526 fov_y = fov_y * 360 / M_PI;
527
528 // warp if underwater
529 contents = CG_PointContents( cg.refdef.vieworg, -1 );
530 if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
531 phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
532 v = WAVE_AMPLITUDE * sin( phase );
533 fov_x += v;
534 fov_y -= v;
535 inwater = qtrue;
536 }
537 else {
538 inwater = qfalse;
539 }
540
541
542 // set it
543 cg.refdef.fov_x = fov_x;
544 cg.refdef.fov_y = fov_y;
545
546 if ( !cg.zoomed ) {
547 cg.zoomSensitivity = 1;
548 } else {
549 cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
550 }
551
552 return inwater;
553 }
554
555
556
557 /*
558 ===============
559 CG_DamageBlendBlob
560
561 ===============
562 */
CG_DamageBlendBlob(void)563 static void CG_DamageBlendBlob( void ) {
564 int t;
565 int maxTime;
566 refEntity_t ent;
567
568 if ( !cg.damageValue ) {
569 return;
570 }
571
572 //if (cg.cameraMode) {
573 // return;
574 //}
575
576 // ragePro systems can't fade blends, so don't obscure the screen
577 if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
578 return;
579 }
580
581 maxTime = DAMAGE_TIME;
582 t = cg.time - cg.damageTime;
583 if ( t <= 0 || t >= maxTime ) {
584 return;
585 }
586
587
588 memset( &ent, 0, sizeof( ent ) );
589 ent.reType = RT_SPRITE;
590 ent.renderfx = RF_FIRST_PERSON;
591
592 VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
593 VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
594 VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
595
596 ent.radius = cg.damageValue * 3;
597 ent.customShader = cgs.media.viewBloodShader;
598 ent.shaderRGBA[0] = 255;
599 ent.shaderRGBA[1] = 255;
600 ent.shaderRGBA[2] = 255;
601 ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) );
602 trap_R_AddRefEntityToScene( &ent );
603 }
604
605
606 /*
607 ===============
608 CG_CalcViewValues
609
610 Sets cg.refdef view values
611 ===============
612 */
CG_CalcViewValues(void)613 static int CG_CalcViewValues( void ) {
614 playerState_t *ps;
615
616 memset( &cg.refdef, 0, sizeof( cg.refdef ) );
617
618 // strings for in game rendering
619 // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) );
620 // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) );
621
622 // calculate size of 3D view
623 CG_CalcVrect();
624
625 ps = &cg.predictedPlayerState;
626 /*
627 if (cg.cameraMode) {
628 vec3_t origin, angles;
629 if (trap_getCameraInfo(cg.time, &origin, &angles)) {
630 VectorCopy(origin, cg.refdef.vieworg);
631 angles[ROLL] = 0;
632 VectorCopy(angles, cg.refdefViewAngles);
633 AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
634 return CG_CalcFov();
635 } else {
636 cg.cameraMode = qfalse;
637 }
638 }
639 */
640 // intermission view
641 if ( ps->pm_type == PM_INTERMISSION ) {
642 VectorCopy( ps->origin, cg.refdef.vieworg );
643 VectorCopy( ps->viewangles, cg.refdefViewAngles );
644 AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
645 return CG_CalcFov();
646 }
647
648 cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
649 cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
650 cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
651 ps->velocity[1] * ps->velocity[1] );
652
653
654 VectorCopy( ps->origin, cg.refdef.vieworg );
655 VectorCopy( ps->viewangles, cg.refdefViewAngles );
656
657 if (cg_cameraOrbit.integer) {
658 if (cg.time > cg.nextOrbitTime) {
659 cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer;
660 cg_thirdPersonAngle.value += cg_cameraOrbit.value;
661 }
662 }
663 // add error decay
664 if ( cg_errorDecay.value > 0 ) {
665 int t;
666 float f;
667
668 t = cg.time - cg.predictedErrorTime;
669 f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
670 if ( f > 0 && f < 1 ) {
671 VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
672 } else {
673 cg.predictedErrorTime = 0;
674 }
675 }
676
677 if ( cg.renderingThirdPerson ) {
678 // back away from character
679 CG_OffsetThirdPersonView();
680 } else {
681 // offset for local bobbing and kicks
682 CG_OffsetFirstPersonView();
683 }
684
685 // position eye reletive to origin
686 AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
687
688 if ( cg.hyperspace ) {
689 cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
690 }
691
692 // field of view
693 return CG_CalcFov();
694 }
695
696
697 /*
698 =====================
699 CG_PowerupTimerSounds
700 =====================
701 */
CG_PowerupTimerSounds(void)702 static void CG_PowerupTimerSounds( void ) {
703 int i;
704 int t;
705
706 // powerup timers going away
707 for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
708 t = cg.snap->ps.powerups[i];
709 if ( t <= cg.time ) {
710 continue;
711 }
712 if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
713 continue;
714 }
715 if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) {
716 trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound );
717 }
718 }
719 }
720
721 /*
722 =====================
723 CG_AddBufferedSound
724 =====================
725 */
CG_AddBufferedSound(sfxHandle_t sfx)726 void CG_AddBufferedSound( sfxHandle_t sfx ) {
727 if ( !sfx )
728 return;
729 cg.soundBuffer[cg.soundBufferIn] = sfx;
730 cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER;
731 if (cg.soundBufferIn == cg.soundBufferOut) {
732 cg.soundBufferOut++;
733 }
734 }
735
736 /*
737 =====================
738 CG_PlayBufferedSounds
739 =====================
740 */
CG_PlayBufferedSounds(void)741 static void CG_PlayBufferedSounds( void ) {
742 if ( cg.soundTime < cg.time ) {
743 if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) {
744 trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER);
745 cg.soundBuffer[cg.soundBufferOut] = 0;
746 cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER;
747 cg.soundTime = cg.time + 750;
748 }
749 }
750 }
751
752 //=========================================================================
753
754 /*
755 =================
756 CG_DrawActiveFrame
757
758 Generates and draws a game scene and status information at the given time.
759 =================
760 */
CG_DrawActiveFrame(int serverTime,stereoFrame_t stereoView,qboolean demoPlayback)761 void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) {
762 int inwater;
763
764 cg.time = serverTime;
765 cg.demoPlayback = demoPlayback;
766
767 // update cvars
768 CG_UpdateCvars();
769
770 // if we are only updating the screen as a loading
771 // pacifier, don't even try to read snapshots
772 if ( cg.infoScreenText[0] != 0 ) {
773 CG_DrawInformation();
774 return;
775 }
776
777 // any looped sounds will be respecified as entities
778 // are added to the render list
779 trap_S_ClearLoopingSounds(qfalse);
780
781 // clear all the render lists
782 trap_R_ClearScene();
783
784 // set up cg.snap and possibly cg.nextSnap
785 CG_ProcessSnapshots();
786
787 // if we haven't received any snapshots yet, all
788 // we can draw is the information screen
789 if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
790 CG_DrawInformation();
791 return;
792 }
793
794 // let the client system know what our weapon and zoom settings are
795 trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity );
796
797 // this counter will be bumped for every valid scene we generate
798 cg.clientFrame++;
799
800 // update cg.predictedPlayerState
801 CG_PredictPlayerState();
802
803 // decide on third person view
804 cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
805
806 // build cg.refdef
807 inwater = CG_CalcViewValues();
808
809 // first person blend blobs, done after AnglesToAxis
810 if ( !cg.renderingThirdPerson ) {
811 CG_DamageBlendBlob();
812 }
813
814 // build the render lists
815 if ( !cg.hyperspace ) {
816 CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct
817 CG_AddMarks();
818 CG_AddParticles ();
819 CG_AddLocalEntities();
820 }
821 CG_AddViewWeapon( &cg.predictedPlayerState );
822
823 // add buffered sounds
824 CG_PlayBufferedSounds();
825
826 // play buffered voice chats
827 CG_PlayBufferedVoiceChats();
828
829 // finish up the rest of the refdef
830 if ( cg.testModelEntity.hModel ) {
831 CG_AddTestModel();
832 }
833 cg.refdef.time = cg.time;
834 memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
835
836 // warning sounds when powerup is wearing off
837 CG_PowerupTimerSounds();
838
839 // update audio positions
840 trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
841
842 // make sure the lagometerSample and frame timing isn't done twice when in stereo
843 if ( stereoView != STEREO_RIGHT ) {
844 cg.frametime = cg.time - cg.oldTime;
845 if ( cg.frametime < 0 ) {
846 cg.frametime = 0;
847 }
848 cg.oldTime = cg.time;
849 CG_AddLagometerFrameInfo();
850 }
851 if (cg_timescale.value != cg_timescaleFadeEnd.value) {
852 if (cg_timescale.value < cg_timescaleFadeEnd.value) {
853 cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
854 if (cg_timescale.value > cg_timescaleFadeEnd.value)
855 cg_timescale.value = cg_timescaleFadeEnd.value;
856 }
857 else {
858 cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
859 if (cg_timescale.value < cg_timescaleFadeEnd.value)
860 cg_timescale.value = cg_timescaleFadeEnd.value;
861 }
862 if (cg_timescaleFadeSpeed.value) {
863 trap_Cvar_Set("timescale", va("%f", cg_timescale.value));
864 }
865 }
866
867 // actually issue the rendering calls
868 CG_DrawActive( stereoView );
869
870 if ( cg_stats.integer ) {
871 CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
872 }
873
874
875 }
876
877