[input] Separate absolute and relative axis inputs

Combining absolute and relative inputs at the binding does not work well
because absolute inputs generally update only when the physical input
updates, so clearing the axis input each frame results in a brief pulse
from the physical input, but relative inputs must be cleared each frame
(where frame here is each time the axis is read) but must accumulate the
relative updates between frames.

Other than the axis mode being incorrect, this seems to work quite
nicely.
This commit is contained in:
Bill Currie 2021-11-23 22:04:17 +09:00
parent 78b4e8217e
commit 1ef99a9130
4 changed files with 88 additions and 34 deletions

View file

@ -31,6 +31,8 @@
#ifndef __QFCC__
#include "QF/mathlib.h"
/*** Recipe for converting an axis to a floating point value.
Absolute axes are converted to the 0..1 range for unbalanced axes, and
@ -81,6 +83,8 @@ typedef enum {
typedef struct in_axis_s {
float value; ///< converted value of the axis
in_axis_mode mode; ///< method used for updating the destination
float abs_input; ///< input from an absolute axis (eg, joystick)
float rel_input; ///< input from a relative axis (eg, mouse)
const char *name;
const char *description;
} in_axis_t;
@ -155,6 +159,7 @@ GNU89INLINE inline float IN_ButtonState (in_button_t *button);
Both steady-state on, and brief clicks are detected.
\param button Pointer to the button being tested.
\return True if the button is currently held or was pulsed on
in the last frame.
\note The edge transitions are cleared, so for each frame, this
@ -167,6 +172,7 @@ GNU89INLINE inline int IN_ButtonPressed (in_button_t *button);
Valid only if the button is still released. A pulsed off does not
count as being released as the button is still held.
\param button Pointer to the button being tested.
\return True if the button is currently released and the release
was in the last frame.
\note The edge transitions are cleared, so for each frame, this
@ -174,6 +180,41 @@ GNU89INLINE inline int IN_ButtonPressed (in_button_t *button);
*/
GNU89INLINE inline int IN_ButtonReleased (in_button_t *button);
/*** Update the axis value based on its mode and clear its relative input.
The absolute and relative inputs are separate because absolute inputs
usually get written when the input actually changes (and thus must not
be cleared each frame), while relative inputs indicate a per-frame delta
and thus must be cleared each frame.
\param axis Pointer to the axis being updated.
\return The resulting output value of the axis.
\note The relative input (\a rel_input) is zeroed.
*/
GNU89INLINE inline float IN_UpdateAxis (in_axis_t *axis);
/*** Update and clamp the axis value (see IN_UpdateAxis())
Like IN_UpdateAxis(), but clamps the final output to the specified range.
This is most useful for \a ina_accumulate axes, but can be used to ensure
\a ina_set axes never exceed a given range.
The absolute and relative inputs are separate because absolute inputs
usually get written when the input actually changes (and thus must not
be cleared each frame), while relative inputs indicate a per-frame delta
and thus must be cleared each frame.
\param axis Pointer to the axis being updated.
\param minval The minimum value to which the axis output will be clamped.
\param minval The minimum value to which the axis output will be clamped.
\return The resulting output value of the axis.
\note The relative input (\a rel_input) is zeroed. The absolute
input is not affected by the clamping, only the output
\a value.
*/
GNU89INLINE inline float IN_ClampAxis (in_axis_t *axis,
float minval, float maxval);
#ifndef IMPLEMENT_INPUT_Funcs
GNU89INLINE inline
#else
@ -227,6 +268,39 @@ IN_ButtonReleased (in_button_t *button)
return (state & (inb_down | inb_edge_up)) == inb_edge_up;
}
#ifndef IMPLEMENT_INPUT_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
float
IN_UpdateAxis (in_axis_t *axis)
{
switch (axis->mode) {
case ina_set:
axis->value = axis->abs_input + axis->rel_input;
break;
case ina_accumulate:
axis->value += axis->abs_input + axis->rel_input;
break;
}
axis->rel_input = 0;
return axis->value;
}
#ifndef IMPLEMENT_INPUT_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
float
IN_ClampAxis (in_axis_t *axis, float minval, float maxval)
{
IN_UpdateAxis (axis);
axis->value = bound (minval, axis->value, maxval);
return axis->value;
}
void IN_ButtonAction (in_button_t *buttin, int id, int pressed);
int IN_RegisterButton (in_button_t *button);

View file

@ -429,6 +429,7 @@ IMT_ProcessAxis (int axis, int value)
if (recipe->curve != 1) {
output = powf (output, recipe->curve);
}
a->axis->rel_input += output;
} else {
int input = bound (minval, value, maxval);
int range = maxval - minval;
@ -458,14 +459,7 @@ IMT_ProcessAxis (int axis, int value)
output = powf (output, recipe->curve);
}
output *= recipe->scale;
}
switch (a->axis->mode) {
case ina_set:
a->axis->value = output;
break;
case ina_accumulate:
a->axis->value += output;
break;
a->axis->abs_input = output;
}
return true;
}

View file

@ -337,19 +337,12 @@ CL_BaseMove (usercmd_t *cmd)
}
*/
cmd->forwardmove -= viewdelta_position_forward.value * m_forward->value;
cmd->sidemove += viewdelta_position_side.value * m_side->value;
cmd->upmove -= viewdelta_position_up.value;
cl.viewstate.angles[PITCH] -= viewdelta_angles_pitch.value * m_pitch->value;
cl.viewstate.angles[YAW] -= viewdelta_angles_yaw.value * m_yaw->value;
cl.viewstate.angles[ROLL] -= viewdelta_angles_roll.value * m_pitch->value;
viewdelta_angles_pitch.value = 0;
viewdelta_angles_yaw.value = 0;
viewdelta_angles_roll.value = 0;
viewdelta_position_forward.value = 0;
viewdelta_position_side.value = 0;
viewdelta_position_up.value = 0;
cmd->forwardmove -= IN_UpdateAxis (&viewdelta_position_forward) * m_forward->value;
cmd->sidemove += IN_UpdateAxis (&viewdelta_position_side) * m_side->value;
cmd->upmove -= IN_UpdateAxis (&viewdelta_position_up);
cl.viewstate.angles[PITCH] -= IN_UpdateAxis (&viewdelta_angles_pitch) * m_pitch->value;
cl.viewstate.angles[YAW] -= IN_UpdateAxis (&viewdelta_angles_yaw) * m_yaw->value;
cl.viewstate.angles[ROLL] -= IN_UpdateAxis (&viewdelta_angles_roll) * m_pitch->value;
if (freelook && !(in_strafe.state & inb_down)) {
cl.viewstate.angles[PITCH]

View file

@ -354,19 +354,12 @@ CL_BaseMove (usercmd_t *cmd)
}
*/
cmd->forwardmove -= viewdelta_position_forward.value * m_forward->value;
cmd->sidemove += viewdelta_position_side.value * m_side->value;
cmd->upmove -= viewdelta_position_up.value;
cl.viewstate.angles[PITCH] -= viewdelta_angles_pitch.value * m_pitch->value;
cl.viewstate.angles[YAW] -= viewdelta_angles_yaw.value * m_yaw->value;
cl.viewstate.angles[ROLL] -= viewdelta_angles_roll.value * m_pitch->value;
viewdelta_angles_pitch.value = 0;
viewdelta_angles_yaw.value = 0;
viewdelta_angles_roll.value = 0;
viewdelta_position_forward.value = 0;
viewdelta_position_side.value = 0;
viewdelta_position_up.value = 0;
cmd->forwardmove -= IN_UpdateAxis (&viewdelta_position_forward) * m_forward->value;
cmd->sidemove += IN_UpdateAxis (&viewdelta_position_side) * m_side->value;
cmd->upmove -= IN_UpdateAxis (&viewdelta_position_up);
cl.viewstate.angles[PITCH] -= IN_UpdateAxis (&viewdelta_angles_pitch) * m_pitch->value;
cl.viewstate.angles[YAW] -= IN_UpdateAxis (&viewdelta_angles_yaw) * m_yaw->value;
cl.viewstate.angles[ROLL] -= IN_UpdateAxis (&viewdelta_angles_roll) * m_pitch->value;
if (freelook && !(in_strafe.state & inb_down)) {
cl.viewstate.angles[PITCH]