More mouse improvements (#1348)

* Check `oldlookdir` before disabling `centering`

When turning off mouselook/padlook, keep view centering enabled until `oldlookdir` is zero, meaning when the player's view actually appears centered.

* Move `viewangleoffset`

* Check dead state set by `P_KillMobj()`

* Smooth composite/mouse turning transitions

Blend interpolated composite input (e.g. keyboard) turning with direct mouse turning to prevent choppy transitions.

* Group inputs by composite/gamepad/mouse

* Update carry/input calculations

This also enables shorttics (lowres_turn) with fast mouse polling.

* Add mouse smoothing toggle

With mouse smoothing disabled (default), mouse movements are immediately reflected in each frame update, with the game simulation following behind. The higher the framerate, the lower the perceived input lag. With mouse smoothing enabled, mouse movements are delayed by up to one tic (~29 ms) due to interpolation. When recording older format demos with reduced turning resolution (or when using `-shorttics`), interpolation may still be preferred for smoother turning.

* Rename "mouse smoothing" to "raw mouse input"

* Skip calculations when using interpolation
This commit is contained in:
ceski 2023-12-23 15:29:10 -08:00 committed by GitHub
parent e0d30bbe6e
commit 1a9d580bd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 288 additions and 215 deletions

View File

@ -174,9 +174,10 @@ int eventhead, eventtail;
// //
void D_PostEvent(event_t *ev) void D_PostEvent(event_t *ev)
{ {
if (ev->type == ev_mouse && !menuactive && gamestate == GS_LEVEL && !paused) if (ev->type == ev_mouse)
{ {
G_MouseMovementResponder(ev); G_MouseMovementResponder(ev);
G_PrepTiccmd();
return; return;
} }
@ -239,9 +240,9 @@ void D_Display (void)
// [AM] Figure out how far into the current tic we're in as a fixed_t. // [AM] Figure out how far into the current tic we're in as a fixed_t.
fractionaltic = I_GetFracTime(); fractionaltic = I_GetFracTime();
if (window_focused) if (!menuactive && gamestate == GS_LEVEL && !paused && mouse_raw_input)
{ {
I_ReadMouse(); I_StartDisplay();
} }
} }

View File

@ -171,19 +171,18 @@ static int mousex;
static int mousey; static int mousey;
boolean dclick; boolean dclick;
// Skip mouse if using a controller and recording in strict mode (DSDA rule). typedef struct carry_s
static boolean skip_mouse = true;
typedef struct cmd_carry_s
{ {
double angle; double angle;
double pitch; double pitch;
double strafe; double side;
double vert; double vert;
} cmd_carry_t; short lowres;
} carry_t;
static cmd_carry_t cmd_prev_carry; static carry_t prevcarry;
static cmd_carry_t cmd_carry; static carry_t carry;
static ticcmd_t basecmd;
boolean joyarray[MAX_JSB+1]; // [FG] support more joystick buttons boolean joyarray[MAX_JSB+1]; // [FG] support more joystick buttons
boolean *joybuttons = &joyarray[1]; // allow [-1] boolean *joybuttons = &joyarray[1]; // allow [-1]
@ -369,93 +368,101 @@ static void G_DemoSkipTics(void)
} }
} }
static int CarryError(double value, double *prev_carry, double *carry) static int CarryError(double value, const double *prevcarry, double *carry)
{ {
const double desired = value + *prev_carry; const double desired = value + *prevcarry;
const int actual = lround(desired); const int actual = lround(desired);
*carry = desired - actual; *carry = desired - actual;
return actual; return actual;
} }
static int CalcMouseAngle(int mousex) static int CarryAngle(double angle)
{ {
if (mouseSensitivity_horiz) return CarryError(angle, &prevcarry.angle, &carry.angle);
{
const double angle = (I_AccelerateMouse(mousex) *
(mouseSensitivity_horiz + 5) * 8 / 10);
return CarryError(angle, &cmd_prev_carry.angle, &cmd_carry.angle);
}
else
{
cmd_prev_carry.angle = 0.0;
cmd_carry.angle = 0.0;
return 0;
}
} }
static int CalcMousePitch(int mousey) static int CarryMousePitch(double pitch)
{ {
if (mouseSensitivity_vert_look) return CarryError(pitch, &prevcarry.pitch, &carry.pitch);
{
const double pitch = (I_AccelerateMouse(mouse_y_invert ? -mousey : mousey) *
(mouseSensitivity_vert_look + 5) / 10);
return CarryError(pitch, &cmd_prev_carry.pitch, &cmd_carry.pitch);
}
else
{
cmd_prev_carry.pitch = 0.0;
cmd_carry.pitch = 0.0;
return 0;
}
} }
static int CalcMouseStrafe(int mousex) static int CarryMouseVert(double vert)
{ {
if (mouseSensitivity_horiz_strafe) return CarryError(vert, &prevcarry.vert, &carry.vert);
{
const double desired = (cmd_prev_carry.strafe + I_AccelerateMouse(mousex) *
(mouseSensitivity_horiz_strafe + 5) * 2 / 10);
const int actual = lround(desired * 0.5) * 2; // Even values only.
cmd_carry.strafe = desired - actual;
return actual;
}
else
{
cmd_prev_carry.strafe = 0.0;
cmd_carry.strafe = 0.0;
return 0;
}
} }
static int CalcMouseVert(int mousey) static int CarryMouseSide(double side)
{ {
if (mouseSensitivity_vert) const double desired = side + prevcarry.side;
{ const int actual = lround(desired * 0.5) * 2; // Even values only.
const double vert = (I_AccelerateMouse(mousey) * carry.side = desired - actual;
(mouseSensitivity_vert + 5) / 10); return actual;
return CarryError(vert, &cmd_prev_carry.vert, &cmd_carry.vert);
}
else
{
cmd_prev_carry.vert = 0.0;
cmd_carry.vert = 0.0;
return 0;
}
} }
void G_MouseMovementResponder(const event_t *ev) static short CarryLowResAngle(short angle)
{ {
if (strictmode && demorecording && skip_mouse) const short desired = angle + prevcarry.lowres;
return; // Round to nearest 256 for single byte turning. From Chocolate Doom.
const short actual = (desired + 128) & 0xFF00;
carry.lowres = desired - actual;
return actual;
}
mousex += ev->data2; static double CalcMouseAngle(int mousex)
mousey += ev->data3; {
if (!mouseSensitivity_horiz)
return 0.0;
return (I_AccelerateMouse(mousex) * (mouseSensitivity_horiz + 5) * 8 / 10);
}
static double CalcMousePitch(int mousey)
{
if (!mouseSensitivity_vert_look)
return 0.0;
return (I_AccelerateMouse(mousey) * direction[mouse_y_invert] *
(mouseSensitivity_vert_look + 5) / 10);
}
static double CalcMouseSide(int mousex)
{
if (!mouseSensitivity_horiz_strafe)
return 0.0;
return (I_AccelerateMouse(mousex) *
(mouseSensitivity_horiz_strafe + 5) * 2 / 10);
}
static double CalcMouseVert(int mousey)
{
if (!mouseSensitivity_vert)
return 0.0;
return (I_AccelerateMouse(mousey) * (mouseSensitivity_vert + 5) / 10);
}
void G_PrepTiccmd(void)
{
ticcmd_t *cmd = &basecmd;
if (!M_InputGameActive(input_strafe)) if (!M_InputGameActive(input_strafe))
localview.angle = CalcMouseAngle(mousex); {
localview.rawangle = -CalcMouseAngle(mousex);
cmd->angleturn = CarryAngle(localview.rawangle);
if (lowres_turn)
{
cmd->angleturn = CarryLowResAngle(cmd->angleturn);
}
localview.angle = cmd->angleturn << 16;
}
if (mouselook) if (mouselook)
localview.pitch = CalcMousePitch(mousey); {
const double pitch = CalcMousePitch(mousey);
cmd->lookdir = CarryMousePitch(pitch);
localview.pitch = cmd->lookdir;
}
} }
// //
@ -467,98 +474,67 @@ void G_MouseMovementResponder(const event_t *ev)
void G_BuildTiccmd(ticcmd_t* cmd) void G_BuildTiccmd(ticcmd_t* cmd)
{ {
boolean strafe; const boolean strafe = M_InputGameActive(input_strafe);
int speed; const boolean turnleft = M_InputGameActive(input_turnleft);
int tspeed; const boolean turnright = M_InputGameActive(input_turnright);
int forward; // [FG] speed key inverts autorun
int side; const int speed = autorun ^ M_InputGameActive(input_speed); // phares
int angle = 0;
int pitch = 0;
int forward = 0;
int side = 0;
int newweapon; // phares int newweapon; // phares
ticcmd_t *base;
extern boolean boom_weapon_state_injection; extern boolean boom_weapon_state_injection;
static boolean done_autoswitch = false; static boolean done_autoswitch = false;
// Assume localview can be used unless mouse input is interrupted by other // Assume localview can be used unless mouse input is interrupted by other
// inputs that apply turning or looking up/down (e.g. keyboard or gamepad). // inputs that apply looking up/down (e.g. gamepad).
localview.useangle = !lowres_turn;
localview.usepitch = true; localview.usepitch = true;
G_DemoSkipTics(); G_DemoSkipTics();
base = I_BaseTiccmd(); // empty, or external driver memcpy(cmd, &basecmd, sizeof(*cmd));
memcpy(cmd, base, sizeof *cmd); memset(&basecmd, 0, sizeof(basecmd));
cmd->consistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; cmd->consistancy = consistancy[consoleplayer][maketic%BACKUPTICS];
strafe = M_InputGameActive(input_strafe); // Composite input
// [FG] speed key inverts autorun
speed = autorun ^ M_InputGameActive(input_speed); // phares
forward = side = 0;
// use two stage accelerative turning
// on the keyboard and joystick
if (M_InputGameActive(input_turnleft) ||
M_InputGameActive(input_turnright))
turnheld += ticdup;
else
turnheld = 0;
if (turnheld < SLOWTURNTICS)
tspeed = 2; // slow turn
else
tspeed = speed;
// turn 180 degrees in one keystroke? // phares // turn 180 degrees in one keystroke? // phares
// | if (STRICTMODE(M_InputGameActive(input_reverse)))
if (STRICTMODE(M_InputGameActive(input_reverse))) // V {
{ angle += QUICKREVERSE;
cmd->angleturn += (short)QUICKREVERSE; // ^ M_InputGameDeactivate(input_reverse);
localview.useangle = false; }
M_InputGameDeactivate(input_reverse); // |
} // phares
// let movement keys cancel each other out // let movement keys cancel each other out
if (turnleft || turnright)
{
turnheld += ticdup;
if (strafe) if (strafe)
{ {
if (M_InputGameActive(input_turnright)) if (turnright)
side += sidemove[speed]; side += sidemove[speed];
if (M_InputGameActive(input_turnleft)) if (turnleft)
side -= sidemove[speed]; side -= sidemove[speed];
if (analog_controls && controller_axes[axis_turn])
{
fixed_t x = axis_move_sens * controller_axes[axis_turn] / 10;
x = direction[invert_turn] * x;
side += FixedMul(sidemove[speed], x);
}
} }
else else
{ {
if (M_InputGameActive(input_turnright)) // use two stage accelerative turning on the keyboard and joystick
{ const int tspeed = ((turnheld < SLOWTURNTICS) ? 2 : speed);
cmd->angleturn -= angleturn[tspeed];
localview.useangle = false;
}
if (M_InputGameActive(input_turnleft))
{
cmd->angleturn += angleturn[tspeed];
localview.useangle = false;
}
if (analog_controls && controller_axes[axis_turn]) if (turnright)
{ angle -= angleturn[tspeed];
fixed_t x = controller_axes[axis_turn]; if (turnleft)
angle += angleturn[tspeed];
// response curve to compensate for lack of near-centered accuracy
x = FixedMul(FixedMul(x, x), x);
x = direction[invert_turn] * axis_turn_sens * x / 10;
cmd->angleturn -= FixedMul(angleturn[1], x);
localview.useangle = false;
}
} }
}
else
{
turnheld = 0;
}
if (M_InputGameActive(input_forward)) if (M_InputGameActive(input_forward))
forward += forwardmove[speed]; forward += forwardmove[speed];
@ -569,14 +545,37 @@ void G_BuildTiccmd(ticcmd_t* cmd)
if (M_InputGameActive(input_strafeleft)) if (M_InputGameActive(input_strafeleft))
side -= sidemove[speed]; side -= sidemove[speed];
// Gamepad
if (analog_controls) if (analog_controls)
{ {
if (controller_axes[axis_turn])
{
if (strafe)
{
fixed_t x = axis_move_sens * controller_axes[axis_turn] / 10;
x = direction[invert_turn] * x;
side += FixedMul(sidemove[speed], x);
}
else
{
fixed_t x = controller_axes[axis_turn];
// response curve to compensate for lack of near-centered accuracy
x = FixedMul(FixedMul(x, x), x);
x = direction[invert_turn] * axis_turn_sens * x / 10;
angle -= FixedMul(angleturn[1], x);
}
}
if (controller_axes[axis_forward]) if (controller_axes[axis_forward])
{ {
fixed_t y = axis_move_sens * controller_axes[axis_forward] / 10; fixed_t y = axis_move_sens * controller_axes[axis_forward] / 10;
y = direction[invert_forward] * y; y = direction[invert_forward] * y;
forward -= FixedMul(forwardmove[speed], y); forward -= FixedMul(forwardmove[speed], y);
} }
if (controller_axes[axis_strafe]) if (controller_axes[axis_strafe])
{ {
fixed_t x = axis_move_sens * controller_axes[axis_strafe] / 10; fixed_t x = axis_move_sens * controller_axes[axis_strafe] / 10;
@ -592,12 +591,63 @@ void G_BuildTiccmd(ticcmd_t* cmd)
y = FixedMul(FixedMul(y, y), y); y = FixedMul(FixedMul(y, y), y);
y = direction[invert_look] * axis_look_sens * y / 10; y = direction[invert_look] * axis_look_sens * y / 10;
cmd->lookdir -= FixedMul(lookspeed[0], y); pitch -= FixedMul(lookspeed[0], y);
localview.usepitch = false;
} }
} }
// buttons // Mouse
if (strafe)
{
const double mouseside = CalcMouseSide(mousex);
side += CarryMouseSide(mouseside);
}
if (!mouselook && !novert)
{
const double mousevert = CalcMouseVert(mousey);
forward += CarryMouseVert(mousevert);
}
// Update/reset
if (angle)
{
angle = CarryAngle(localview.rawangle + angle);
if (lowres_turn)
{
angle = CarryLowResAngle(angle);
}
localview.ticangleturn = angle - cmd->angleturn;
cmd->angleturn = angle;
}
if (pitch)
{
cmd->lookdir = pitch;
localview.usepitch = false;
}
if (forward > MAXPLMOVE)
forward = MAXPLMOVE;
else if (forward < -MAXPLMOVE)
forward = -MAXPLMOVE;
if (side > MAXPLMOVE)
side = MAXPLMOVE;
else if (side < -MAXPLMOVE)
side = -MAXPLMOVE;
cmd->forwardmove = forward;
cmd->sidemove = side;
mousex = mousey = 0;
localview.angle = 0;
localview.pitch = 0;
localview.rawangle = 0.0;
prevcarry = carry;
// Buttons
cmd->chatchar = HU_dequeueChatChar(); cmd->chatchar = HU_dequeueChatChar();
if (M_InputGameActive(input_fire)) if (M_InputGameActive(input_fire))
@ -716,33 +766,6 @@ void G_BuildTiccmd(ticcmd_t* cmd)
cmd->buttons |= BT_USE; cmd->buttons |= BT_USE;
} }
if (strafe)
side += CalcMouseStrafe(mousex);
else
cmd->angleturn -= localview.angle;
if (mouselook)
cmd->lookdir += localview.pitch;
else if (!novert)
forward += CalcMouseVert(mousey);
mousex = mousey = 0;
localview.angle = 0;
localview.pitch = 0;
cmd_prev_carry = cmd_carry;
if (forward > MAXPLMOVE)
forward = MAXPLMOVE;
else if (forward < -MAXPLMOVE)
forward = -MAXPLMOVE;
if (side > MAXPLMOVE)
side = MAXPLMOVE;
else if (side < -MAXPLMOVE)
side = -MAXPLMOVE;
cmd->forwardmove += forward;
cmd->sidemove += side;
// special buttons // special buttons
if (sendpause) if (sendpause)
{ {
@ -768,26 +791,6 @@ void G_BuildTiccmd(ticcmd_t* cmd)
sendjoin = false; sendjoin = false;
cmd->buttons |= BT_JOIN; cmd->buttons |= BT_JOIN;
} }
// low-res turning
if (lowres_turn)
{
static signed short carry = 0;
signed short desired_angleturn;
desired_angleturn = cmd->angleturn + carry;
// round angleturn to the nearest 256 unit boundary
// for recording demos with single byte values for turn
cmd->angleturn = (desired_angleturn + 128) & 0xff00;
// Carry forward the error from the reduced resolution to the
// next tic, so that successive small movements can accumulate.
carry = desired_angleturn - cmd->angleturn;
}
} }
// //
@ -898,8 +901,9 @@ static void G_DoLoadLevel(void)
memset (gamekeydown, 0, sizeof(gamekeydown)); memset (gamekeydown, 0, sizeof(gamekeydown));
mousex = mousey = 0; mousex = mousey = 0;
memset(&localview, 0, sizeof(localview)); memset(&localview, 0, sizeof(localview));
memset(&cmd_carry, 0, sizeof(cmd_carry)); memset(&carry, 0, sizeof(carry));
memset(&cmd_prev_carry, 0, sizeof(cmd_prev_carry)); memset(&prevcarry, 0, sizeof(prevcarry));
memset(&basecmd, 0, sizeof(basecmd));
sendpause = sendsave = paused = false; sendpause = sendsave = paused = false;
// [FG] array size! // [FG] array size!
memset (mousearray, 0, sizeof(mousearray)); memset (mousearray, 0, sizeof(mousearray));
@ -970,7 +974,6 @@ static boolean G_StrictModeSkipEvent(event_t *ev)
{ {
first_event = false; first_event = false;
enable_mouse = true; enable_mouse = true;
skip_mouse = false;
} }
return !enable_mouse; return !enable_mouse;
@ -991,6 +994,23 @@ static boolean G_StrictModeSkipEvent(event_t *ev)
return false; return false;
} }
boolean G_MouseMovementResponder(event_t *ev)
{
if (G_StrictModeSkipEvent(ev))
{
return true;
}
if (ev->type == ev_mouse)
{
mousex += ev->data2;
mousey += ev->data3;
return true;
}
return false;
}
// //
// G_Responder // G_Responder
// Get info needed to make ticcmd_ts for the players. // Get info needed to make ticcmd_ts for the players.
@ -1105,8 +1125,10 @@ boolean G_Responder(event_t* ev)
return true; return true;
} }
if (G_StrictModeSkipEvent(ev)) if (G_MouseMovementResponder(ev))
{
return true; // eat events return true; // eat events
}
switch (ev->type) switch (ev->type)
{ {
@ -1130,10 +1152,6 @@ boolean G_Responder(event_t* ev)
mousebuttons[ev->data1] = false; mousebuttons[ev->data1] = false;
return true; return true;
case ev_mouse:
G_MouseMovementResponder(ev);
return true; // eat events
case ev_joyb_down: case ev_joyb_down:
if (ev->data1 < MAX_JSB) if (ev->data1 < MAX_JSB)
joybuttons[ev->data1] = true; joybuttons[ev->data1] = true;

View File

@ -30,7 +30,8 @@
#define MBF21_GAME_OPTION_SIZE (21 + MBF21_COMP_TOTAL) #define MBF21_GAME_OPTION_SIZE (21 + MBF21_COMP_TOTAL)
void G_MouseMovementResponder(const event_t *ev); void G_PrepTiccmd(void);
boolean G_MouseMovementResponder(event_t *ev);
boolean G_Responder(event_t *ev); boolean G_Responder(event_t *ev);
boolean G_CheckDemoStatus(void); boolean G_CheckDemoStatus(void);
void G_DeathMatchSpawnPlayer(int playernum); void G_DeathMatchSpawnPlayer(int playernum);

View File

@ -403,7 +403,6 @@ void I_ReadMouse(void)
int x, y; int x, y;
static event_t ev; static event_t ev;
SDL_PumpEvents();
SDL_GetRelativeMouseState(&x, &y); SDL_GetRelativeMouseState(&x, &y);
if (x != 0 || y != 0) if (x != 0 || y != 0)

View File

@ -45,6 +45,8 @@ void I_StartFrame (void);
void I_StartTic (void); void I_StartTic (void);
void I_StartDisplay(void);
// Asynchronous interrupt functions should maintain private queues // Asynchronous interrupt functions should maintain private queues
// that are read by the synchronous functions // that are read by the synchronous functions
// to be converted into events. // to be converted into events.

View File

@ -418,15 +418,25 @@ static void I_GetEvent(void)
// //
void I_StartTic (void) void I_StartTic (void)
{ {
I_GetEvent();
if (window_focused) if (window_focused)
{ {
I_ReadMouse(); I_ReadMouse();
} }
I_GetEvent();
I_UpdateJoystick(); I_UpdateJoystick();
} }
void I_StartDisplay(void)
{
if (window_focused)
{
SDL_PumpEvents();
I_ReadMouse();
}
}
// //
// I_StartFrame // I_StartFrame
// //

View File

@ -3941,6 +3941,7 @@ enum {
gen5_mouse3, gen5_mouse3,
gen5_mouse_accel, gen5_mouse_accel,
gen5_mouse_accel_threshold, gen5_mouse_accel_threshold,
gen5_mouse_raw_input,
gen5_end1, gen5_end1,
gen5_title2, gen5_title2,
@ -4165,6 +4166,9 @@ setup_menu_t gen_settings5[] = { // General Settings screen5
{"Mouse threshold", S_NUM, m_null, M_X, {"Mouse threshold", S_NUM, m_null, M_X,
M_Y + gen5_mouse_accel_threshold * M_SPC, {"mouse_acceleration_threshold"}}, M_Y + gen5_mouse_accel_threshold * M_SPC, {"mouse_acceleration_threshold"}},
{"Raw mouse input", S_YESNO, m_null, M_X,
M_Y+ gen5_mouse_raw_input * M_SPC, {"mouse_raw_input"}},
{"", S_SKIP, m_null, M_X, M_Y + gen5_end1*M_SPC}, {"", S_SKIP, m_null, M_X, M_Y + gen5_end1*M_SPC},
{"Miscellaneous" ,S_SKIP|S_TITLE, m_null, M_X, M_Y + gen5_title2*M_SPC}, {"Miscellaneous" ,S_SKIP|S_TITLE, m_null, M_X, M_Y + gen5_title2*M_SPC},

View File

@ -2059,6 +2059,13 @@ default_t defaults[] = {
"adjust mouse acceleration threshold" "adjust mouse acceleration threshold"
}, },
{
"mouse_raw_input",
(config_t *) &mouse_raw_input, NULL,
{1}, {0, 1}, number, ss_none, wad_no,
"Raw mouse input for turning/looking (0 = Interpolate, 1 = Raw)"
},
// [FG] invert vertical axis // [FG] invert vertical axis
{ {
"mouse_y_invert", "mouse_y_invert",

View File

@ -190,6 +190,12 @@ void P_MovePlayer (player_t* player)
mo->angle += cmd->angleturn << 16; mo->angle += cmd->angleturn << 16;
onground = mo->z <= mo->floorz; onground = mo->z <= mo->floorz;
if (player == &players[consoleplayer])
{
localview.ticangle += localview.ticangleturn << 16;
localview.ticangleturn = 0;
}
// killough 10/98: // killough 10/98:
// //
// We must apply thrust to the player and bobbing separately, to avoid // We must apply thrust to the player and bobbing separately, to avoid
@ -353,6 +359,11 @@ void P_PlayerThink (player_t* player)
player->oldlookdir = player->lookdir; player->oldlookdir = player->lookdir;
player->oldrecoilpitch = player->recoilpitch; player->oldrecoilpitch = player->recoilpitch;
if (player == &players[consoleplayer])
{
localview.oldticangle = localview.ticangle;
}
// killough 2/8/98, 3/21/98: // killough 2/8/98, 3/21/98:
// (this code is necessary despite questions raised elsewhere in a comment) // (this code is necessary despite questions raised elsewhere in a comment)
@ -386,7 +397,11 @@ void P_PlayerThink (player_t* player)
if (abs(player->lookdir) < 8 * MLOOKUNIT) if (abs(player->lookdir) < 8 * MLOOKUNIT)
{ {
player->lookdir = 0; player->lookdir = 0;
player->centering = false;
if (player->oldlookdir == 0)
{
player->centering = false;
}
} }
player->slope = PLAYER_SLOPE(player); player->slope = PLAYER_SLOPE(player);

View File

@ -48,6 +48,7 @@ fixed_t projection;
fixed_t viewx, viewy, viewz; fixed_t viewx, viewy, viewz;
angle_t viewangle; angle_t viewangle;
localview_t localview; localview_t localview;
boolean mouse_raw_input;
fixed_t viewcos, viewsin; fixed_t viewcos, viewsin;
player_t *viewplayer; player_t *viewplayer;
extern lighttable_t **walllights; extern lighttable_t **walllights;
@ -648,10 +649,12 @@ void R_SetupFrame (player_t *player)
leveltime > oldleveltime) leveltime > oldleveltime)
{ {
const boolean use_localview = ( const boolean use_localview = (
// Don't use localview when interpolation is preferred.
mouse_raw_input &&
// Don't use localview if the player is spying. // Don't use localview if the player is spying.
player == &players[consoleplayer] && player == &players[consoleplayer] &&
// Don't use localview if the player is dead. // Don't use localview if the player is dead.
player->health > 0 && player->playerstate != PST_DEAD &&
// Don't use localview if the player just teleported. // Don't use localview if the player just teleported.
!player->mo->reactiontime && !player->mo->reactiontime &&
// Don't use localview if a demo is playing. // Don't use localview if a demo is playing.
@ -667,12 +670,16 @@ void R_SetupFrame (player_t *player)
// Use localview unless the player or game is in an invalid state or if // Use localview unless the player or game is in an invalid state or if
// mouse input was interrupted, in which case fall back to interpolation. // mouse input was interrupted, in which case fall back to interpolation.
if (localview.useangle && use_localview) if (use_localview)
viewangle = player->mo->angle - ((short)localview.angle << FRACBITS) + viewangleoffset; {
viewangle = (player->mo->angle + localview.angle - localview.ticangle +
R_InterpolateAngle(localview.oldticangle, localview.ticangle,
fractionaltic));
}
else else
viewangle = R_InterpolateAngle(player->mo->oldangle, player->mo->angle, fractionaltic) + viewangleoffset; viewangle = R_InterpolateAngle(player->mo->oldangle, player->mo->angle, fractionaltic);
if (localview.usepitch && use_localview && !player->centering && player->lookdir) if (localview.usepitch && use_localview && !player->centering)
pitch = (player->lookdir + localview.pitch) / MLOOKUNIT; pitch = (player->lookdir + localview.pitch) / MLOOKUNIT;
else else
pitch = (player->oldlookdir + (player->lookdir - player->oldlookdir) * FIXED2DOUBLE(fractionaltic)) / MLOOKUNIT; pitch = (player->oldlookdir + (player->lookdir - player->oldlookdir) * FIXED2DOUBLE(fractionaltic)) / MLOOKUNIT;
@ -682,13 +689,17 @@ void R_SetupFrame (player_t *player)
} }
else else
{ {
viewx = player->mo->x; viewx = player->mo->x;
viewy = player->mo->y; viewy = player->mo->y;
viewz = player->viewz; // [FG] moved here viewz = player->viewz; // [FG] moved here
viewangle = player->mo->angle + viewangleoffset; viewangle = player->mo->angle;
// [crispy] pitch is actual lookdir and weapon pitch // [crispy] pitch is actual lookdir and weapon pitch
pitch = player->lookdir / MLOOKUNIT + player->recoilpitch; pitch = player->lookdir / MLOOKUNIT + player->recoilpitch;
} }
// 3-screen display mode.
viewangle += viewangleoffset;
extralight = player->extralight; extralight = player->extralight;
extralight += STRICTMODE(LIGHTBRIGHT * extra_level_brightness); extralight += STRICTMODE(LIGHTBRIGHT * extra_level_brightness);

View File

@ -81,6 +81,8 @@ extern lighttable_t *fixedcolormap;
// range of [0.0, 1.0). Used for interpolation. // range of [0.0, 1.0). Used for interpolation.
extern fixed_t fractionaltic; extern fixed_t fractionaltic;
extern boolean mouse_raw_input;
// [AM] Interpolate between two angles. // [AM] Interpolate between two angles.
angle_t R_InterpolateAngle(angle_t oangle, angle_t nangle, fixed_t scale); angle_t R_InterpolateAngle(angle_t oangle, angle_t nangle, fixed_t scale);

View File

@ -90,8 +90,11 @@ extern side_t *sides;
typedef struct localview_s typedef struct localview_s
{ {
boolean useangle;
boolean usepitch; boolean usepitch;
angle_t oldticangle;
angle_t ticangle;
int ticangleturn;
double rawangle;
int angle; int angle;
int pitch; int pitch;
} localview_t; } localview_t;