/* | |
Copyright (C) 1996-1997 Id Software, Inc. | |
This program is free software; you can redistribute it and/or | |
modify it under the terms of the GNU General Public License | |
as published by the Free Software Foundation; either version 2 | |
of the License, or (at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
See the GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
*/ | |
// view.c -- player eye positioning | |
#include "quakedef.h" | |
#include "r_local.h" | |
/* | |
The view is allowed to move slightly from it's true position for bobbing, | |
but if it exceeds 8 pixels linear distance (spherical, not box), the list of | |
entities sent from the server may not include everything in the pvs, especially | |
when crossing a water boudnary. | |
*/ | |
cvar_t lcd_x = {"lcd_x","0"}; | |
cvar_t lcd_yaw = {"lcd_yaw","0"}; | |
cvar_t scr_ofsx = {"scr_ofsx","0", false}; | |
cvar_t scr_ofsy = {"scr_ofsy","0", false}; | |
cvar_t scr_ofsz = {"scr_ofsz","0", false}; | |
cvar_t cl_rollspeed = {"cl_rollspeed", "200"}; | |
cvar_t cl_rollangle = {"cl_rollangle", "2.0"}; | |
cvar_t cl_bob = {"cl_bob","0.02", false}; | |
cvar_t cl_bobcycle = {"cl_bobcycle","0.6", false}; | |
cvar_t cl_bobup = {"cl_bobup","0.5", false}; | |
cvar_t v_kicktime = {"v_kicktime", "0.5", false}; | |
cvar_t v_kickroll = {"v_kickroll", "0.6", false}; | |
cvar_t v_kickpitch = {"v_kickpitch", "0.6", false}; | |
cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", false}; | |
cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", false}; | |
cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", false}; | |
cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", false}; | |
cvar_t v_iroll_level = {"v_iroll_level", "0.1", false}; | |
cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", false}; | |
cvar_t v_idlescale = {"v_idlescale", "0", false}; | |
cvar_t crosshair = {"crosshair", "0", true}; | |
cvar_t cl_crossx = {"cl_crossx", "0", false}; | |
cvar_t cl_crossy = {"cl_crossy", "0", false}; | |
cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100", false}; | |
float v_dmg_time, v_dmg_roll, v_dmg_pitch; | |
extern int in_forward, in_forward2, in_back; | |
/* | |
=============== | |
V_CalcRoll | |
Used by view and sv_user | |
=============== | |
*/ | |
vec3_t forward, right, up; | |
float V_CalcRoll (vec3_t angles, vec3_t velocity) | |
{ | |
float sign; | |
float side; | |
float value; | |
AngleVectors (angles, forward, right, up); | |
side = DotProduct (velocity, right); | |
sign = side < 0 ? -1 : 1; | |
side = fabs(side); | |
value = cl_rollangle.value; | |
// if (cl.inwater) | |
// value *= 6; | |
if (side < cl_rollspeed.value) | |
side = side * value / cl_rollspeed.value; | |
else | |
side = value; | |
return side*sign; | |
} | |
/* | |
=============== | |
V_CalcBob | |
=============== | |
*/ | |
float V_CalcBob (void) | |
{ | |
float bob; | |
float cycle; | |
cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value; | |
cycle /= cl_bobcycle.value; | |
if (cycle < cl_bobup.value) | |
cycle = M_PI * cycle / cl_bobup.value; | |
else | |
cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); | |
// bob is proportional to velocity in the xy plane | |
// (don't count Z, or jumping messes it up) | |
bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value; | |
//Con_Printf ("speed: %5.1f\n", Length(cl.velocity)); | |
bob = bob*0.3 + bob*0.7*sin(cycle); | |
if (bob > 4) | |
bob = 4; | |
else if (bob < -7) | |
bob = -7; | |
return bob; | |
} | |
//============================================================================= | |
cvar_t v_centermove = {"v_centermove", "0.15", false}; | |
cvar_t v_centerspeed = {"v_centerspeed","500"}; | |
void V_StartPitchDrift (void) | |
{ | |
#if 1 | |
if (cl.laststop == cl.time) | |
{ | |
return; // something else is keeping it from drifting | |
} | |
#endif | |
if (cl.nodrift || !cl.pitchvel) | |
{ | |
cl.pitchvel = v_centerspeed.value; | |
cl.nodrift = false; | |
cl.driftmove = 0; | |
} | |
} | |
void V_StopPitchDrift (void) | |
{ | |
cl.laststop = cl.time; | |
cl.nodrift = true; | |
cl.pitchvel = 0; | |
} | |
/* | |
=============== | |
V_DriftPitch | |
Moves the client pitch angle towards cl.idealpitch sent by the server. | |
If the user is adjusting pitch manually, either with lookup/lookdown, | |
mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. | |
Drifting is enabled when the center view key is hit, mlook is released and | |
lookspring is non 0, or when | |
=============== | |
*/ | |
void V_DriftPitch (void) | |
{ | |
float delta, move; | |
if (noclip_anglehack || !cl.onground || cls.demoplayback ) | |
{ | |
cl.driftmove = 0; | |
cl.pitchvel = 0; | |
return; | |
} | |
// don't count small mouse motion | |
if (cl.nodrift) | |
{ | |
if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value) | |
cl.driftmove = 0; | |
else | |
cl.driftmove += host_frametime; | |
if ( cl.driftmove > v_centermove.value) | |
{ | |
V_StartPitchDrift (); | |
} | |
return; | |
} | |
delta = cl.idealpitch - cl.viewangles[PITCH]; | |
if (!delta) | |
{ | |
cl.pitchvel = 0; | |
return; | |
} | |
move = host_frametime * cl.pitchvel; | |
cl.pitchvel += host_frametime * v_centerspeed.value; | |
//Con_Printf ("move: %f (%f)\n", move, host_frametime); | |
if (delta > 0) | |
{ | |
if (move > delta) | |
{ | |
cl.pitchvel = 0; | |
move = delta; | |
} | |
cl.viewangles[PITCH] += move; | |
} | |
else if (delta < 0) | |
{ | |
if (move > -delta) | |
{ | |
cl.pitchvel = 0; | |
move = -delta; | |
} | |
cl.viewangles[PITCH] -= move; | |
} | |
} | |
/* | |
============================================================================== | |
PALETTE FLASHES | |
============================================================================== | |
*/ | |
cshift_t cshift_empty = { {130,80,50}, 0 }; | |
cshift_t cshift_water = { {130,80,50}, 128 }; | |
cshift_t cshift_slime = { {0,25,5}, 150 }; | |
cshift_t cshift_lava = { {255,80,0}, 150 }; | |
cvar_t v_gamma = {"gamma", "1", true}; | |
byte gammatable[256]; // palette is sent through this | |
#ifdef GLQUAKE | |
byte ramps[3][256]; | |
float v_blend[4]; // rgba 0.0 - 1.0 | |
#endif // GLQUAKE | |
void BuildGammaTable (float g) | |
{ | |
int i, inf; | |
if (g == 1.0) | |
{ | |
for (i=0 ; i<256 ; i++) | |
gammatable[i] = i; | |
return; | |
} | |
for (i=0 ; i<256 ; i++) | |
{ | |
inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5; | |
if (inf < 0) | |
inf = 0; | |
if (inf > 255) | |
inf = 255; | |
gammatable[i] = inf; | |
} | |
} | |
/* | |
================= | |
V_CheckGamma | |
================= | |
*/ | |
qboolean V_CheckGamma (void) | |
{ | |
static float oldgammavalue; | |
if (v_gamma.value == oldgammavalue) | |
return false; | |
oldgammavalue = v_gamma.value; | |
BuildGammaTable (v_gamma.value); | |
vid.recalc_refdef = 1; // force a surface cache flush | |
return true; | |
} | |
/* | |
=============== | |
V_ParseDamage | |
=============== | |
*/ | |
void V_ParseDamage (void) | |
{ | |
int armor, blood; | |
vec3_t from; | |
int i; | |
vec3_t forward, right, up; | |
entity_t *ent; | |
float side; | |
float count; | |
armor = MSG_ReadByte (); | |
blood = MSG_ReadByte (); | |
for (i=0 ; i<3 ; i++) | |
from[i] = MSG_ReadCoord (); | |
count = blood*0.5 + armor*0.5; | |
if (count < 10) | |
count = 10; | |
cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame | |
cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; | |
if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) | |
cl.cshifts[CSHIFT_DAMAGE].percent = 0; | |
if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) | |
cl.cshifts[CSHIFT_DAMAGE].percent = 150; | |
if (armor > blood) | |
{ | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; | |
} | |
else if (armor) | |
{ | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; | |
} | |
else | |
{ | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; | |
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; | |
} | |
// | |
// calculate view angle kicks | |
// | |
ent = &cl_entities[cl.viewentity]; | |
VectorSubtract (from, ent->origin, from); | |
VectorNormalize (from); | |
AngleVectors (ent->angles, forward, right, up); | |
side = DotProduct (from, right); | |
v_dmg_roll = count*side*v_kickroll.value; | |
side = DotProduct (from, forward); | |
v_dmg_pitch = count*side*v_kickpitch.value; | |
v_dmg_time = v_kicktime.value; | |
} | |
/* | |
================== | |
V_cshift_f | |
================== | |
*/ | |
void V_cshift_f (void) | |
{ | |
cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); | |
cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); | |
cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); | |
cshift_empty.percent = atoi(Cmd_Argv(4)); | |
} | |
/* | |
================== | |
V_BonusFlash_f | |
When you run over an item, the server sends this command | |
================== | |
*/ | |
void V_BonusFlash_f (void) | |
{ | |
cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; | |
cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; | |
cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; | |
cl.cshifts[CSHIFT_BONUS].percent = 50; | |
} | |
/* | |
============= | |
V_SetContentsColor | |
Underwater, lava, etc each has a color shift | |
============= | |
*/ | |
void V_SetContentsColor (int contents) | |
{ | |
switch (contents) | |
{ | |
case CONTENTS_EMPTY: | |
case CONTENTS_SOLID: | |
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; | |
break; | |
case CONTENTS_LAVA: | |
cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; | |
break; | |
case CONTENTS_SLIME: | |
cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; | |
break; | |
default: | |
cl.cshifts[CSHIFT_CONTENTS] = cshift_water; | |
} | |
} | |
/* | |
============= | |
V_CalcPowerupCshift | |
============= | |
*/ | |
void V_CalcPowerupCshift (void) | |
{ | |
if (cl.items & IT_QUAD) | |
{ | |
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; | |
cl.cshifts[CSHIFT_POWERUP].percent = 30; | |
} | |
else if (cl.items & IT_SUIT) | |
{ | |
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; | |
cl.cshifts[CSHIFT_POWERUP].percent = 20; | |
} | |
else if (cl.items & IT_INVISIBILITY) | |
{ | |
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; | |
cl.cshifts[CSHIFT_POWERUP].percent = 100; | |
} | |
else if (cl.items & IT_INVULNERABILITY) | |
{ | |
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; | |
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; | |
cl.cshifts[CSHIFT_POWERUP].percent = 30; | |
} | |
else | |
cl.cshifts[CSHIFT_POWERUP].percent = 0; | |
} | |
/* | |
============= | |
V_CalcBlend | |
============= | |
*/ | |
#ifdef GLQUAKE | |
void V_CalcBlend (void) | |
{ | |
float r, g, b, a, a2; | |
int j; | |
r = 0; | |
g = 0; | |
b = 0; | |
a = 0; | |
for (j=0 ; j<NUM_CSHIFTS ; j++) | |
{ | |
if (!gl_cshiftpercent.value) | |
continue; | |
a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0; | |
// a2 = cl.cshifts[j].percent/255.0; | |
if (!a2) | |
continue; | |
a = a + a2*(1-a); | |
//Con_Printf ("j:%i a:%f\n", j, a); | |
a2 = a2/a; | |
r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2; | |
g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2; | |
b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2; | |
} | |
v_blend[0] = r/255.0; | |
v_blend[1] = g/255.0; | |
v_blend[2] = b/255.0; | |
v_blend[3] = a; | |
if (v_blend[3] > 1) | |
v_blend[3] = 1; | |
if (v_blend[3] < 0) | |
v_blend[3] = 0; | |
} | |
#endif | |
/* | |
============= | |
V_UpdatePalette | |
============= | |
*/ | |
#ifdef GLQUAKE | |
void V_UpdatePalette (void) | |
{ | |
int i, j; | |
qboolean new; | |
byte *basepal, *newpal; | |
byte pal[768]; | |
float r,g,b,a; | |
int ir, ig, ib; | |
qboolean force; | |
V_CalcPowerupCshift (); | |
new = false; | |
for (i=0 ; i<NUM_CSHIFTS ; i++) | |
{ | |
if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) | |
{ | |
new = true; | |
cl.prev_cshifts[i].percent = cl.cshifts[i].percent; | |
} | |
for (j=0 ; j<3 ; j++) | |
if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) | |
{ | |
new = true; | |
cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; | |
} | |
} | |
// drop the damage value | |
cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; | |
if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) | |
cl.cshifts[CSHIFT_DAMAGE].percent = 0; | |
// drop the bonus value | |
cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; | |
if (cl.cshifts[CSHIFT_BONUS].percent <= 0) | |
cl.cshifts[CSHIFT_BONUS].percent = 0; | |
force = V_CheckGamma (); | |
if (!new && !force) | |
return; | |
V_CalcBlend (); | |
a = v_blend[3]; | |
r = 255*v_blend[0]*a; | |
g = 255*v_blend[1]*a; | |
b = 255*v_blend[2]*a; | |
a = 1-a; | |
for (i=0 ; i<256 ; i++) | |
{ | |
ir = i*a + r; | |
ig = i*a + g; | |
ib = i*a + b; | |
if (ir > 255) | |
ir = 255; | |
if (ig > 255) | |
ig = 255; | |
if (ib > 255) | |
ib = 255; | |
ramps[0][i] = gammatable[ir]; | |
ramps[1][i] = gammatable[ig]; | |
ramps[2][i] = gammatable[ib]; | |
} | |
basepal = host_basepal; | |
newpal = pal; | |
for (i=0 ; i<256 ; i++) | |
{ | |
ir = basepal[0]; | |
ig = basepal[1]; | |
ib = basepal[2]; | |
basepal += 3; | |
newpal[0] = ramps[0][ir]; | |
newpal[1] = ramps[1][ig]; | |
newpal[2] = ramps[2][ib]; | |
newpal += 3; | |
} | |
VID_ShiftPalette (pal); | |
} | |
#else // !GLQUAKE | |
void V_UpdatePalette (void) | |
{ | |
int i, j; | |
qboolean new; | |
byte *basepal, *newpal; | |
byte pal[768]; | |
int r,g,b; | |
qboolean force; | |
V_CalcPowerupCshift (); | |
new = false; | |
for (i=0 ; i<NUM_CSHIFTS ; i++) | |
{ | |
if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) | |
{ | |
new = true; | |
cl.prev_cshifts[i].percent = cl.cshifts[i].percent; | |
} | |
for (j=0 ; j<3 ; j++) | |
if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) | |
{ | |
new = true; | |
cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; | |
} | |
} | |
// drop the damage value | |
cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; | |
if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) | |
cl.cshifts[CSHIFT_DAMAGE].percent = 0; | |
// drop the bonus value | |
cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; | |
if (cl.cshifts[CSHIFT_BONUS].percent <= 0) | |
cl.cshifts[CSHIFT_BONUS].percent = 0; | |
force = V_CheckGamma (); | |
if (!new && !force) | |
return; | |
basepal = host_basepal; | |
newpal = pal; | |
for (i=0 ; i<256 ; i++) | |
{ | |
r = basepal[0]; | |
g = basepal[1]; | |
b = basepal[2]; | |
basepal += 3; | |
for (j=0 ; j<NUM_CSHIFTS ; j++) | |
{ | |
r += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[0]-r))>>8; | |
g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8; | |
b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8; | |
} | |
newpal[0] = gammatable[r]; | |
newpal[1] = gammatable[g]; | |
newpal[2] = gammatable[b]; | |
newpal += 3; | |
} | |
VID_ShiftPalette (pal); | |
} | |
#endif // !GLQUAKE | |
/* | |
============================================================================== | |
VIEW RENDERING | |
============================================================================== | |
*/ | |
float angledelta (float a) | |
{ | |
a = anglemod(a); | |
if (a > 180) | |
a -= 360; | |
return a; | |
} | |
/* | |
================== | |
CalcGunAngle | |
================== | |
*/ | |
void CalcGunAngle (void) | |
{ | |
float yaw, pitch, move; | |
static float oldyaw = 0; | |
static float oldpitch = 0; | |
yaw = r_refdef.viewangles[YAW]; | |
pitch = -r_refdef.viewangles[PITCH]; | |
yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; | |
if (yaw > 10) | |
yaw = 10; | |
if (yaw < -10) | |
yaw = -10; | |
pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; | |
if (pitch > 10) | |
pitch = 10; | |
if (pitch < -10) | |
pitch = -10; | |
move = host_frametime*20; | |
if (yaw > oldyaw) | |
{ | |
if (oldyaw + move < yaw) | |
yaw = oldyaw + move; | |
} | |
else | |
{ | |
if (oldyaw - move > yaw) | |
yaw = oldyaw - move; | |
} | |
if (pitch > oldpitch) | |
{ | |
if (oldpitch + move < pitch) | |
pitch = oldpitch + move; | |
} | |
else | |
{ | |
if (oldpitch - move > pitch) | |
pitch = oldpitch - move; | |
} | |
oldyaw = yaw; | |
oldpitch = pitch; | |
cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; | |
cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); | |
cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; | |
cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; | |
cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; | |
} | |
/* | |
============== | |
V_BoundOffsets | |
============== | |
*/ | |
void V_BoundOffsets (void) | |
{ | |
entity_t *ent; | |
ent = &cl_entities[cl.viewentity]; | |
// absolutely bound refresh reletive to entity clipping hull | |
// so the view can never be inside a solid wall | |
if (r_refdef.vieworg[0] < ent->origin[0] - 14) | |
r_refdef.vieworg[0] = ent->origin[0] - 14; | |
else if (r_refdef.vieworg[0] > ent->origin[0] + 14) | |
r_refdef.vieworg[0] = ent->origin[0] + 14; | |
if (r_refdef.vieworg[1] < ent->origin[1] - 14) | |
r_refdef.vieworg[1] = ent->origin[1] - 14; | |
else if (r_refdef.vieworg[1] > ent->origin[1] + 14) | |
r_refdef.vieworg[1] = ent->origin[1] + 14; | |
if (r_refdef.vieworg[2] < ent->origin[2] - 22) | |
r_refdef.vieworg[2] = ent->origin[2] - 22; | |
else if (r_refdef.vieworg[2] > ent->origin[2] + 30) | |
r_refdef.vieworg[2] = ent->origin[2] + 30; | |
} | |
/* | |
============== | |
V_AddIdle | |
Idle swaying | |
============== | |
*/ | |
void V_AddIdle (void) | |
{ | |
r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; | |
r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; | |
r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; | |
} | |
/* | |
============== | |
V_CalcViewRoll | |
Roll is induced by movement and damage | |
============== | |
*/ | |
void V_CalcViewRoll (void) | |
{ | |
float side; | |
side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity); | |
r_refdef.viewangles[ROLL] += side; | |
if (v_dmg_time > 0) | |
{ | |
r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; | |
r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; | |
v_dmg_time -= host_frametime; | |
} | |
if (cl.stats[STAT_HEALTH] <= 0) | |
{ | |
r_refdef.viewangles[ROLL] = 80; // dead view angle | |
return; | |
} | |
} | |
/* | |
================== | |
V_CalcIntermissionRefdef | |
================== | |
*/ | |
void V_CalcIntermissionRefdef (void) | |
{ | |
entity_t *ent, *view; | |
float old; | |
// ent is the player model (visible when out of body) | |
ent = &cl_entities[cl.viewentity]; | |
// view is the weapon model (only visible from inside body) | |
view = &cl.viewent; | |
VectorCopy (ent->origin, r_refdef.vieworg); | |
VectorCopy (ent->angles, r_refdef.viewangles); | |
view->model = NULL; | |
// allways idle in intermission | |
old = v_idlescale.value; | |
v_idlescale.value = 1; | |
V_AddIdle (); | |
v_idlescale.value = old; | |
} | |
/* | |
================== | |
V_CalcRefdef | |
================== | |
*/ | |
void V_CalcRefdef (void) | |
{ | |
entity_t *ent, *view; | |
int i; | |
vec3_t forward, right, up; | |
vec3_t angles; | |
float bob; | |
static float oldz = 0; | |
V_DriftPitch (); | |
// ent is the player model (visible when out of body) | |
ent = &cl_entities[cl.viewentity]; | |
// view is the weapon model (only visible from inside body) | |
view = &cl.viewent; | |
// transform the view offset by the model's matrix to get the offset from | |
// model origin for the view | |
ent->angles[YAW] = cl.viewangles[YAW]; // the model should face | |
// the view dir | |
ent->angles[PITCH] = -cl.viewangles[PITCH]; // the model should face | |
// the view dir | |
bob = V_CalcBob (); | |
// refresh position | |
VectorCopy (ent->origin, r_refdef.vieworg); | |
r_refdef.vieworg[2] += cl.viewheight + bob; | |
// never let it sit exactly on a node line, because a water plane can | |
// dissapear when viewed with the eye exactly on it. | |
// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis | |
r_refdef.vieworg[0] += 1.0/32; | |
r_refdef.vieworg[1] += 1.0/32; | |
r_refdef.vieworg[2] += 1.0/32; | |
VectorCopy (cl.viewangles, r_refdef.viewangles); | |
V_CalcViewRoll (); | |
V_AddIdle (); | |
// offsets | |
angles[PITCH] = -ent->angles[PITCH]; // because entity pitches are | |
// actually backward | |
angles[YAW] = ent->angles[YAW]; | |
angles[ROLL] = ent->angles[ROLL]; | |
AngleVectors (angles, forward, right, up); | |
for (i=0 ; i<3 ; i++) | |
r_refdef.vieworg[i] += scr_ofsx.value*forward[i] | |
+ scr_ofsy.value*right[i] | |
+ scr_ofsz.value*up[i]; | |
V_BoundOffsets (); | |
// set up gun position | |
VectorCopy (cl.viewangles, view->angles); | |
CalcGunAngle (); | |
VectorCopy (ent->origin, view->origin); | |
view->origin[2] += cl.viewheight; | |
for (i=0 ; i<3 ; i++) | |
{ | |
view->origin[i] += forward[i]*bob*0.4; | |
// view->origin[i] += right[i]*bob*0.4; | |
// view->origin[i] += up[i]*bob*0.8; | |
} | |
view->origin[2] += bob; | |
// fudge position around to keep amount of weapon visible | |
// roughly equal with different FOV | |
#if 0 | |
if (cl.model_precache[cl.stats[STAT_WEAPON]] && strcmp (cl.model_precache[cl.stats[STAT_WEAPON]]->name, "progs/v_shot2.mdl")) | |
#endif | |
if (scr_viewsize.value == 110) | |
view->origin[2] += 1; | |
else if (scr_viewsize.value == 100) | |
view->origin[2] += 2; | |
else if (scr_viewsize.value == 90) | |
view->origin[2] += 1; | |
else if (scr_viewsize.value == 80) | |
view->origin[2] += 0.5; | |
view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; | |
view->frame = cl.stats[STAT_WEAPONFRAME]; | |
view->colormap = vid.colormap; | |
// set up the refresh position | |
VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles); | |
// smooth out stair step ups | |
if (cl.onground && ent->origin[2] - oldz > 0) | |
{ | |
float steptime; | |
steptime = cl.time - cl.oldtime; | |
if (steptime < 0) | |
//FIXME I_Error ("steptime < 0"); | |
steptime = 0; | |
oldz += steptime * 80; | |
if (oldz > ent->origin[2]) | |
oldz = ent->origin[2]; | |
if (ent->origin[2] - oldz > 12) | |
oldz = ent->origin[2] - 12; | |
r_refdef.vieworg[2] += oldz - ent->origin[2]; | |
view->origin[2] += oldz - ent->origin[2]; | |
} | |
else | |
oldz = ent->origin[2]; | |
if (chase_active.value) | |
Chase_Update (); | |
} | |
/* | |
================== | |
V_RenderView | |
The player's clipping box goes from (-16 -16 -24) to (16 16 32) from | |
the entity origin, so any view position inside that will be valid | |
================== | |
*/ | |
extern vrect_t scr_vrect; | |
void V_RenderView (void) | |
{ | |
if (con_forcedup) | |
return; | |
// don't allow cheats in multiplayer | |
if (cl.maxclients > 1) | |
{ | |
Cvar_Set ("scr_ofsx", "0"); | |
Cvar_Set ("scr_ofsy", "0"); | |
Cvar_Set ("scr_ofsz", "0"); | |
} | |
if (cl.intermission) | |
{ // intermission / finale rendering | |
V_CalcIntermissionRefdef (); | |
} | |
else | |
{ | |
if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ ) | |
V_CalcRefdef (); | |
} | |
R_PushDlights (); | |
if (lcd_x.value) | |
{ | |
// | |
// render two interleaved views | |
// | |
int i; | |
vid.rowbytes <<= 1; | |
vid.aspect *= 0.5; | |
r_refdef.viewangles[YAW] -= lcd_yaw.value; | |
for (i=0 ; i<3 ; i++) | |
r_refdef.vieworg[i] -= right[i]*lcd_x.value; | |
R_RenderView (); | |
vid.buffer += vid.rowbytes>>1; | |
R_PushDlights (); | |
r_refdef.viewangles[YAW] += lcd_yaw.value*2; | |
for (i=0 ; i<3 ; i++) | |
r_refdef.vieworg[i] += 2*right[i]*lcd_x.value; | |
R_RenderView (); | |
vid.buffer -= vid.rowbytes>>1; | |
r_refdef.vrect.height <<= 1; | |
vid.rowbytes >>= 1; | |
vid.aspect *= 2; | |
} | |
else | |
{ | |
R_RenderView (); | |
} | |
#ifndef GLQUAKE | |
if (crosshair.value) | |
Draw_Character (scr_vrect.x + scr_vrect.width/2 + cl_crossx.value, | |
scr_vrect.y + scr_vrect.height/2 + cl_crossy.value, '+'); | |
#endif | |
} | |
//============================================================================ | |
/* | |
============= | |
V_Init | |
============= | |
*/ | |
void V_Init (void) | |
{ | |
Cmd_AddCommand ("v_cshift", V_cshift_f); | |
Cmd_AddCommand ("bf", V_BonusFlash_f); | |
Cmd_AddCommand ("centerview", V_StartPitchDrift); | |
Cvar_RegisterVariable (&lcd_x); | |
Cvar_RegisterVariable (&lcd_yaw); | |
Cvar_RegisterVariable (&v_centermove); | |
Cvar_RegisterVariable (&v_centerspeed); | |
Cvar_RegisterVariable (&v_iyaw_cycle); | |
Cvar_RegisterVariable (&v_iroll_cycle); | |
Cvar_RegisterVariable (&v_ipitch_cycle); | |
Cvar_RegisterVariable (&v_iyaw_level); | |
Cvar_RegisterVariable (&v_iroll_level); | |
Cvar_RegisterVariable (&v_ipitch_level); | |
Cvar_RegisterVariable (&v_idlescale); | |
Cvar_RegisterVariable (&crosshair); | |
Cvar_RegisterVariable (&cl_crossx); | |
Cvar_RegisterVariable (&cl_crossy); | |
Cvar_RegisterVariable (&gl_cshiftpercent); | |
Cvar_RegisterVariable (&scr_ofsx); | |
Cvar_RegisterVariable (&scr_ofsy); | |
Cvar_RegisterVariable (&scr_ofsz); | |
Cvar_RegisterVariable (&cl_rollspeed); | |
Cvar_RegisterVariable (&cl_rollangle); | |
Cvar_RegisterVariable (&cl_bob); | |
Cvar_RegisterVariable (&cl_bobcycle); | |
Cvar_RegisterVariable (&cl_bobup); | |
Cvar_RegisterVariable (&v_kicktime); | |
Cvar_RegisterVariable (&v_kickroll); | |
Cvar_RegisterVariable (&v_kickpitch); | |
BuildGammaTable (1.0); // no gamma yet | |
Cvar_RegisterVariable (&v_gamma); | |
} | |