2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // view.c -- player eye positioning
26 The view is allowed to move slightly from it's true position for bobbing,
27 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
28 entities sent from the server may not include everything in the pvs, especially
29 when crossing a water boudnary.
33 cvar_t cl_rollspeed = {"cl_rollspeed", "200"};
34 cvar_t cl_rollangle = {"cl_rollangle", "2.0"};
36 cvar_t cl_bob = {"cl_bob","0.02", false};
37 cvar_t cl_bobcycle = {"cl_bobcycle","0.6", false};
38 cvar_t cl_bobup = {"cl_bobup","0.5", false};
40 cvar_t v_kicktime = {"v_kicktime", "0.5", false};
41 cvar_t v_kickroll = {"v_kickroll", "0.6", false};
42 cvar_t v_kickpitch = {"v_kickpitch", "0.6", false};
44 cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", false};
45 cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", false};
46 cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", false};
47 cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", false};
48 cvar_t v_iroll_level = {"v_iroll_level", "0.1", false};
49 cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", false};
51 cvar_t v_idlescale = {"v_idlescale", "0", false};
53 cvar_t crosshair = {"crosshair", "0", true};
54 cvar_t cl_crossx = {"cl_crossx", "0", false};
55 cvar_t cl_crossy = {"cl_crossy", "0", false};
57 //cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100", false};
58 cvar_t gl_polyblend = {"gl_polyblend", "1", true};
60 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
62 extern int in_forward, in_forward2, in_back;
69 Used by view and sv_user
72 vec3_t forward, right, up;
74 float V_CalcRoll (vec3_t angles, vec3_t velocity)
80 AngleVectors (angles, forward, right, up);
81 side = DotProduct (velocity, right);
82 sign = side < 0 ? -1 : 1;
85 value = cl_rollangle.value;
89 if (side < cl_rollspeed.value)
90 side = side * value / cl_rollspeed.value;
105 float V_CalcBob (void)
110 cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value;
111 cycle /= cl_bobcycle.value;
112 if (cycle < cl_bobup.value)
113 cycle = M_PI * cycle / cl_bobup.value;
115 cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
117 // bob is proportional to velocity in the xy plane
118 // (don't count Z, or jumping messes it up)
120 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
121 //Con_Printf ("speed: %5.1f\n", Length(cl.velocity));
122 bob = bob*0.3 + bob*0.7*sin(cycle);
132 //=============================================================================
135 cvar_t v_centermove = {"v_centermove", "0.15", false};
136 cvar_t v_centerspeed = {"v_centerspeed","500"};
139 void V_StartPitchDrift (void)
142 if (cl.laststop == cl.time)
144 return; // something else is keeping it from drifting
147 if (cl.nodrift || !cl.pitchvel)
149 cl.pitchvel = v_centerspeed.value;
155 void V_StopPitchDrift (void)
157 cl.laststop = cl.time;
166 Moves the client pitch angle towards cl.idealpitch sent by the server.
168 If the user is adjusting pitch manually, either with lookup/lookdown,
169 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
171 Drifting is enabled when the center view key is hit, mlook is released and
172 lookspring is non 0, or when
175 void V_DriftPitch (void)
179 if (noclip_anglehack || !cl.onground || cls.demoplayback )
186 // don't count small mouse motion
189 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
192 cl.driftmove += host_frametime;
194 if ( cl.driftmove > v_centermove.value)
196 V_StartPitchDrift ();
201 delta = cl.idealpitch - cl.viewangles[PITCH];
209 move = host_frametime * cl.pitchvel;
210 cl.pitchvel += host_frametime * v_centerspeed.value;
212 //Con_Printf ("move: %f (%f)\n", move, host_frametime);
221 cl.viewangles[PITCH] += move;
230 cl.viewangles[PITCH] -= move;
239 ==============================================================================
243 ==============================================================================
247 cshift_t cshift_empty = { {130,80,50}, 0 };
248 cshift_t cshift_water = { {130,80,50}, 128 };
249 cshift_t cshift_slime = { {0,25,5}, 150 };
250 cshift_t cshift_lava = { {255,80,0}, 150 };
253 float v_blend[4]; // rgba 0.0 - 1.0
260 void V_ParseDamage (void)
265 vec3_t forward, right, up;
270 armor = MSG_ReadByte ();
271 blood = MSG_ReadByte ();
272 for (i=0 ; i<3 ; i++)
273 from[i] = MSG_ReadCoord ();
275 count = blood*0.5 + armor*0.5;
279 cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame
281 if (gl_polyblend.value)
283 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
284 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
285 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
286 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
287 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
291 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
292 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
293 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
297 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
298 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
299 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
303 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
304 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
305 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
310 // calculate view angle kicks
312 ent = &cl_entities[cl.viewentity];
314 VectorSubtract (from, ent->origin, from);
315 VectorNormalize (from);
317 AngleVectors (ent->angles, forward, right, up);
319 side = DotProduct (from, right);
320 v_dmg_roll = count*side*v_kickroll.value;
322 side = DotProduct (from, forward);
323 v_dmg_pitch = count*side*v_kickpitch.value;
325 v_dmg_time = v_kicktime.value;
334 void V_cshift_f (void)
336 cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
337 cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
338 cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
339 cshift_empty.percent = atoi(Cmd_Argv(4));
347 When you run over an item, the server sends this command
350 void V_BonusFlash_f (void)
352 if (gl_polyblend.value)
354 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
355 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
356 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
357 cl.cshifts[CSHIFT_BONUS].percent = 50;
365 Underwater, lava, etc each has a color shift
368 void V_SetContentsColor (int contents)
371 c = &cl.cshifts[CSHIFT_CONTENTS]; // just to shorten the code below
372 if (!gl_polyblend.value)
381 //cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
382 c->destcolor[0] = cshift_empty.destcolor[0];
383 c->destcolor[1] = cshift_empty.destcolor[1];
384 c->destcolor[2] = cshift_empty.destcolor[2];
385 c->percent = cshift_empty.percent;
388 //cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
389 c->destcolor[0] = cshift_lava.destcolor[0];
390 c->destcolor[1] = cshift_lava.destcolor[1];
391 c->destcolor[2] = cshift_lava.destcolor[2];
392 c->percent = cshift_lava.percent;
395 //cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
396 c->destcolor[0] = cshift_slime.destcolor[0];
397 c->destcolor[1] = cshift_slime.destcolor[1];
398 c->destcolor[2] = cshift_slime.destcolor[2];
399 c->percent = cshift_slime.percent;
402 //cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
403 c->destcolor[0] = cshift_water.destcolor[0];
404 c->destcolor[1] = cshift_water.destcolor[1];
405 c->destcolor[2] = cshift_water.destcolor[2];
406 c->percent = cshift_water.percent;
415 void V_CalcPowerupCshift (void)
417 if (!gl_polyblend.value)
419 cl.cshifts[CSHIFT_POWERUP].percent = 0;
422 if (cl.items & IT_QUAD)
424 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
425 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
426 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
427 cl.cshifts[CSHIFT_POWERUP].percent = 30;
429 else if (cl.items & IT_SUIT)
431 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
432 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
433 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
434 cl.cshifts[CSHIFT_POWERUP].percent = 20;
436 else if (cl.items & IT_INVISIBILITY)
438 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
439 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
440 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
441 cl.cshifts[CSHIFT_POWERUP].percent = 100;
443 else if (cl.items & IT_INVULNERABILITY)
445 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
446 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
447 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
448 cl.cshifts[CSHIFT_POWERUP].percent = 30;
451 cl.cshifts[CSHIFT_POWERUP].percent = 0;
459 // LordHavoc: fixed V_CalcBlend
460 void V_CalcBlend (void)
462 float r, g, b, a, a2;
470 // if (gl_cshiftpercent.value)
472 for (j=0 ; j<NUM_CSHIFTS ; j++)
474 // a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
475 a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
481 r += (cl.cshifts[j].destcolor[0]-r) * a2;
482 g += (cl.cshifts[j].destcolor[1]-g) * a2;
483 b += (cl.cshifts[j].destcolor[2]-b) * a2;
484 a = 1 - (1 - a) * (1 - a2); // correct alpha multiply... took a while to find it on the web
486 // saturate color (to avoid blending in black)
496 v_blend[0] = bound(0, r * (1.0/255.0), 1);
497 v_blend[1] = bound(0, g * (1.0/255.0), 1);
498 v_blend[2] = bound(0, b * (1.0/255.0), 1);
499 v_blend[3] = bound(0, a , 1);
507 void V_UpdateBlends (void)
512 V_CalcPowerupCshift ();
516 for (i=0 ; i<NUM_CSHIFTS ; i++)
518 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
521 cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
523 for (j=0 ; j<3 ; j++)
524 if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
527 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
531 // drop the damage value
532 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
533 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
534 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
536 // drop the bonus value
537 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
538 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
539 cl.cshifts[CSHIFT_BONUS].percent = 0;
548 ==============================================================================
552 ==============================================================================
555 float angledelta (float a)
568 void CalcGunAngle (void)
570 float yaw, pitch, move;
571 static float oldyaw = 0;
572 static float oldpitch = 0;
574 yaw = r_refdef.viewangles[YAW];
575 pitch = -r_refdef.viewangles[PITCH];
577 yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
582 pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
587 move = host_frametime*20;
590 if (oldyaw + move < yaw)
595 if (oldyaw - move > yaw)
599 if (pitch > oldpitch)
601 if (oldpitch + move < pitch)
602 pitch = oldpitch + move;
606 if (oldpitch - move > pitch)
607 pitch = oldpitch - move;
613 cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
614 cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
616 cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
617 cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
618 cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
626 void V_BoundOffsets (void)
630 ent = &cl_entities[cl.viewentity];
632 // absolutely bound refresh reletive to entity clipping hull
633 // so the view can never be inside a solid wall
635 if (r_refdef.vieworg[0] < ent->origin[0] - 14)
636 r_refdef.vieworg[0] = ent->origin[0] - 14;
637 else if (r_refdef.vieworg[0] > ent->origin[0] + 14)
638 r_refdef.vieworg[0] = ent->origin[0] + 14;
639 if (r_refdef.vieworg[1] < ent->origin[1] - 14)
640 r_refdef.vieworg[1] = ent->origin[1] - 14;
641 else if (r_refdef.vieworg[1] > ent->origin[1] + 14)
642 r_refdef.vieworg[1] = ent->origin[1] + 14;
643 if (r_refdef.vieworg[2] < ent->origin[2] - 22)
644 r_refdef.vieworg[2] = ent->origin[2] - 22;
645 else if (r_refdef.vieworg[2] > ent->origin[2] + 30)
646 r_refdef.vieworg[2] = ent->origin[2] + 30;
656 void V_AddIdle (void)
658 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
659 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
660 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
668 Roll is induced by movement and damage
671 void V_CalcViewRoll (void)
675 side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity);
676 r_refdef.viewangles[ROLL] += side;
680 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
681 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
682 v_dmg_time -= host_frametime;
685 if (cl.stats[STAT_HEALTH] <= 0)
687 r_refdef.viewangles[ROLL] = 80; // dead view angle
696 V_CalcIntermissionRefdef
700 void V_CalcIntermissionRefdef (void)
702 entity_t *ent, *view;
705 // ent is the player model (visible when out of body)
706 ent = &cl_entities[cl.viewentity];
707 // view is the weapon model (only visible from inside body)
710 VectorCopy (ent->origin, r_refdef.vieworg);
711 VectorCopy (ent->angles, r_refdef.viewangles);
714 // allways idle in intermission
715 old = v_idlescale.value;
716 v_idlescale.value = 1;
718 v_idlescale.value = old;
727 extern qboolean intimerefresh;
728 void V_CalcRefdef (void)
730 entity_t *ent, *view;
732 vec3_t forward, right, up;
735 static float oldz = 0;
739 // ent is the player model (visible when out of body)
740 ent = &cl_entities[cl.viewentity];
741 // view is the weapon model (only visible from inside body)
745 // transform the view offset by the model's matrix to get the offset from
746 // model origin for the view
747 if (!chase_active.value) // LordHavoc: get rid of angle problems in chase_active mode
749 ent->angles[YAW] = cl.viewangles[YAW]; // the model should face the view dir
750 ent->angles[PITCH] = -cl.viewangles[PITCH]; // the model should face the view dir
757 VectorCopy (ent->origin, r_refdef.vieworg);
758 r_refdef.vieworg[2] += cl.viewheight + bob;
760 // never let it sit exactly on a node line, because a water plane can
761 // dissapear when viewed with the eye exactly on it.
762 // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
763 r_refdef.vieworg[0] += 1.0/32;
764 r_refdef.vieworg[1] += 1.0/32;
765 r_refdef.vieworg[2] += 1.0/32;
769 VectorCopy (cl.viewangles, r_refdef.viewangles);
775 angles[PITCH] = -ent->angles[PITCH]; // because entity pitches are
777 angles[YAW] = ent->angles[YAW];
778 angles[ROLL] = ent->angles[ROLL];
780 AngleVectors (angles, forward, right, up);
784 // set up gun position
785 VectorCopy (cl.viewangles, view->angles);
789 VectorCopy (ent->origin, view->origin);
790 view->origin[2] += cl.viewheight;
792 for (i=0 ; i<3 ; i++)
794 view->origin[i] += forward[i]*bob*0.4;
795 // view->origin[i] += right[i]*bob*0.4;
796 // view->origin[i] += up[i]*bob*0.8;
798 view->origin[2] += bob;
800 // fudge position around to keep amount of weapon visible
801 // roughly equal with different FOV
803 view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
804 view->frame = cl.stats[STAT_WEAPONFRAME];
805 view->colormap = -1; // no special coloring
807 // set up the refresh position
810 VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
813 // smooth out stair step ups
814 if (cl.onground && ent->origin[2] - oldz > 0)
818 steptime = cl.time - cl.oldtime;
820 //FIXME I_Error ("steptime < 0");
823 oldz += steptime * 80;
824 if (oldz > ent->origin[2])
825 oldz = ent->origin[2];
826 if (ent->origin[2] - oldz > 12)
827 oldz = ent->origin[2] - 12;
828 r_refdef.vieworg[2] += oldz - ent->origin[2];
829 view->origin[2] += oldz - ent->origin[2];
832 oldz = ent->origin[2];
834 if (chase_active.value)
842 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
843 the entity origin, so any view position inside that will be valid
846 void V_RenderView (void)
852 { // intermission / finale rendering
853 V_CalcIntermissionRefdef ();
857 if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ )
864 //============================================================================
873 Cmd_AddCommand ("v_cshift", V_cshift_f);
874 Cmd_AddCommand ("bf", V_BonusFlash_f);
875 Cmd_AddCommand ("centerview", V_StartPitchDrift);
877 Cvar_RegisterVariable (&v_centermove);
878 Cvar_RegisterVariable (&v_centerspeed);
880 Cvar_RegisterVariable (&v_iyaw_cycle);
881 Cvar_RegisterVariable (&v_iroll_cycle);
882 Cvar_RegisterVariable (&v_ipitch_cycle);
883 Cvar_RegisterVariable (&v_iyaw_level);
884 Cvar_RegisterVariable (&v_iroll_level);
885 Cvar_RegisterVariable (&v_ipitch_level);
887 Cvar_RegisterVariable (&v_idlescale);
888 Cvar_RegisterVariable (&crosshair);
889 Cvar_RegisterVariable (&cl_crossx);
890 Cvar_RegisterVariable (&cl_crossy);
891 // Cvar_RegisterVariable (&gl_cshiftpercent);
892 Cvar_RegisterVariable (&gl_polyblend);
894 Cvar_RegisterVariable (&cl_rollspeed);
895 Cvar_RegisterVariable (&cl_rollangle);
896 Cvar_RegisterVariable (&cl_bob);
897 Cvar_RegisterVariable (&cl_bobcycle);
898 Cvar_RegisterVariable (&cl_bobup);
900 Cvar_RegisterVariable (&v_kicktime);
901 Cvar_RegisterVariable (&v_kickroll);
902 Cvar_RegisterVariable (&v_kickpitch);