input: let InputDevice manage axis remapping, add C_twist axis

This commit is contained in:
rdb 2017-12-22 15:08:39 +01:00
parent ef9803b25e
commit 12cfc93b79
8 changed files with 152 additions and 70 deletions

View File

@ -371,17 +371,10 @@ init_device() {
}
if (has_axes) {
AxisRange range;
range._scale = 1.0;
range._bias = 0.0;
_axis_ranges.resize(ABS_CNT, range);
for (int i = 0; i < num_bits; ++i) {
if (test_bit(i, axes)) {
ControlAxis axis = C_none;
if (i >= ABS_HAT0X) {
set_control_map(i, C_none);
// Emulate D-Pad buttons if necessary.
if (i == ABS_HAT0X && emulate_dpad) {
_dpad_x_axis = i;
@ -395,38 +388,25 @@ init_device() {
_buttons.push_back(ButtonState(GamepadButton::dpad_up()));
_buttons.push_back(ButtonState(GamepadButton::dpad_down()));
} else if (i == ABS_HAT0X) {
set_control_map(i, C_hat_x);
axis = C_hat_x;
} else if (i == ABS_HAT0Y) {
set_control_map(i, C_hat_y);
axis = C_hat_y;
}
} else {
set_control_map(i, axis_map[i]);
axis = axis_map[i];
//cerr << "Axis " << axis_map[i] << " is mapped by the driver to " << i << "\n";
}
// Check the initial value and ranges.
struct input_absinfo absinfo;
if (ioctl(_fd, EVIOCGABS(i), &absinfo) >= 0) {
double factor, bias;
if (absinfo.minimum < 0) {
// Centered, eg. for sticks.
factor = 2.0 / (absinfo.maximum - absinfo.minimum);
bias = (absinfo.maximum + absinfo.minimum) / (double)(absinfo.minimum - absinfo.maximum);
} else {
// 0-based, eg. for triggers.
factor = 1.0 / absinfo.maximum;
bias = 0.0;
}
// Flip Y axis to match Windows implementation.
if (i == ABS_Y || i == ABS_RY) {
factor = -factor;
bias = -bias;
swap(absinfo.minimum, absinfo.maximum);
}
_axis_ranges[i]._scale = factor;
_axis_ranges[i]._bias = bias;
_controls[i].state = fma(absinfo.value, factor, bias);
add_control(axis, absinfo.minimum, absinfo.maximum);
control_changed(axis, absinfo.value);
if (absinfo.value != 0) {
all_values_zero = false;
@ -562,13 +542,13 @@ process_events() {
case EV_ABS:
if (code == _dpad_x_axis) {
set_button_state(_dpad_left_button, events[i].value < 0);
set_button_state(_dpad_left_button+1, events[i].value > 0);
button_changed(_dpad_left_button, events[i].value < 0);
button_changed(_dpad_left_button+1, events[i].value > 0);
} else if (code == _dpad_y_axis) {
set_button_state(_dpad_up_button, events[i].value < 0);
set_button_state(_dpad_up_button+1, events[i].value > 0);
button_changed(_dpad_up_button, events[i].value < 0);
button_changed(_dpad_up_button+1, events[i].value > 0);
}
set_control_state(code, fma(events[i].value, _axis_ranges[code]._scale, _axis_ranges[code]._bias));
control_changed(code, events[i].value);
break;
case EV_KEY:

View File

@ -44,12 +44,6 @@ private:
int _ff_strong;
int _ff_weak;
struct AxisRange {
double _scale;
double _bias;
};
pvector<AxisRange> _axis_ranges;
// These are used for D-pad emulation.
int _dpad_x_axis;
int _dpad_y_axis;

View File

@ -461,6 +461,8 @@ INLINE InputDevice::AnalogState::
AnalogState() :
axis(C_none),
state(0.0),
known(false)
known(false),
_scale(1.0),
_bias(0.0)
{
}

View File

@ -114,6 +114,45 @@ get_pointer_events() {
return result;
}
/**
* Called by the implementation to add a new known control.
*/
void InputDevice::
add_control(ControlAxis axis, int minimum, int maximum, bool centered) {
AnalogState state;
state.axis = axis;
if (centered) {
// Centered, eg. for sticks.
state._scale = 2.0 / (maximum - minimum);
state._bias = (maximum + minimum) / (double)(minimum - maximum);
} else {
// 0-based, eg. for triggers.
state._scale = 1.0 / maximum;
state._bias = 0.0;
}
_controls.push_back(state);
}
/**
* Called by the implementation to add a new known control. This version
* tries to guess whether the control is centered or not.
*/
void InputDevice::
add_control(ControlAxis axis, int minimum, int maximum) {
bool centered = (minimum < 0)
|| axis == C_left_x
|| axis == C_left_y
|| axis == C_right_x
|| axis == C_right_y
|| axis == C_x
|| axis == C_y
|| axis == C_wheel
|| axis == C_twist
|| axis == C_rudder;
add_control(axis, minimum, maximum, centered);
}
/**
* Records that a mouse movement has taken place.
*/
@ -165,6 +204,11 @@ pointer_moved(double x, double y, double time) {
_pointer_data._xpos += x;
_pointer_data._ypos += y;
if (device_cat.is_spam() && (x != 0 || y != 0)) {
device_cat.spam()
<< "Pointer moved by " << x << " x " << y << "\n";
}
if (_enable_pointer_events) {
int seq = _event_sequence++;
if (_pointer_events.is_null()) {
@ -184,7 +228,7 @@ pointer_moved(double x, double y, double time) {
* while this call is made.
*/
void InputDevice::
set_button_state(int index, bool down) {
button_changed(int index, bool down) {
nassertv(_lock.debug_is_locked());
nassertv(index >= 0);
if (index >= (int)_buttons.size()) {
@ -244,6 +288,34 @@ set_control_state(int index, double state) {
_controls[index].known = true;
}
/**
* Like set_control_state, but instead passes a raw, unscaled value.
*/
void InputDevice::
control_changed(int index, int state) {
nassertv(_lock.debug_is_locked());
nassertv(index >= 0);
if (index >= (int)_controls.size()) {
_controls.resize(index + 1, AnalogState());
}
double new_state = fma((double)state, _controls[index]._scale, _controls[index]._bias);
if (device_cat.is_spam() && _controls[index].state != new_state) {
device_cat.spam()
<< "Changed control " << index;
if (_controls[index].axis != C_none) {
device_cat.spam(false) << " (" << _controls[index].axis << ")";
}
device_cat.spam(false) << " to " << new_state << " (raw value " << state << ")\n";
}
_controls[index].state = new_state;
_controls[index].known = true;
}
/**
* Records that a tracker movement has taken place.
*/
@ -467,6 +539,9 @@ operator << (ostream &out, InputDevice::DeviceClass dc) {
case InputDevice::DC_hmd:
out << "hmd";
break;
case InputDevice::DC_COUNT:
break;
}
return out;
}
@ -518,6 +593,10 @@ operator << (ostream &out, InputDevice::ControlAxis axis) {
out << "throttle";
break;
case InputDevice::C_twist:
out << "twist";
break;
case InputDevice::C_rudder:
out << "rudder";
break;

View File

@ -108,6 +108,7 @@ PUBLISHED:
C_y,
C_trigger,
C_throttle,
C_twist,
C_rudder,
C_hat_x,
C_hat_y,
@ -183,11 +184,18 @@ PUBLISHED:
virtual void output(ostream &out) const;
protected:
// Called during the constructor to add new controls or buttons
void add_control(ControlAxis axis, int minimum, int maximum, bool centered);
void add_control(ControlAxis axis, int minimum, int maximum);
void set_pointer(bool inwin, double x, double y, double time);
void set_pointer_out_of_window(double time);
void pointer_moved(double x, double y, double time);
void set_button_state(int index, bool down);
void button_changed(int index, bool down);
void control_changed(int index, int value);
void set_control_state(int index, double state);
void set_tracker(const LPoint3 &pos, const LOrientation &orient, double time);
virtual void do_set_vibration(double low, double high);
@ -271,6 +279,10 @@ PUBLISHED:
ControlAxis axis;
double state;
bool known;
public:
double _scale;
double _bias;
};
typedef pvector<AnalogState> Controls;
Controls _controls;

View File

@ -322,6 +322,18 @@ open_device() {
break;
}
_controls[i].axis = axis;
if (axis == C_left_trigger || axis == C_right_trigger || axis == C_trigger) {
// We'd like to use 0.0 to indicate the resting position.
_controls[i]._scale = 1.0 / 65534.0;
_controls[i]._bias = 0.5;
} else if (axis == C_left_y || axis == C_right_y || axis == C_y) {
_controls[i]._scale = 1.0 / -32767.0;
_controls[i]._bias = 0.0;
} else {
_controls[i]._scale = 1.0 / 32767.0;
_controls[i]._bias = 0.0;
}
}
}
@ -436,27 +448,18 @@ process_events() {
int index = events[i].number;
if (events[i].type & JS_EVENT_BUTTON) {
set_button_state(index, (events[i].value != 0));
button_changed(index, (events[i].value != 0));
} else if (events[i].type & JS_EVENT_AXIS) {
if (index == _dpad_x_axis) {
set_button_state(_dpad_left_button, events[i].value < -1000);
set_button_state(_dpad_left_button+1, events[i].value > 1000);
button_changed(_dpad_left_button, events[i].value < -1000);
button_changed(_dpad_left_button+1, events[i].value > 1000);
} else if (index == _dpad_y_axis) {
set_button_state(_dpad_up_button, events[i].value < -1000);
set_button_state(_dpad_up_button+1, events[i].value > 1000);
button_changed(_dpad_up_button, events[i].value < -1000);
button_changed(_dpad_up_button+1, events[i].value > 1000);
}
ControlAxis axis = _controls[index].axis;
if (axis == C_left_trigger || axis == C_right_trigger || axis == C_trigger) {
// We'd like to use 0.0 to indicate the resting position.
set_control_state(index, (events[i].value + 32767) / 65534.0);
} else if (axis == C_left_y || axis == C_right_y || axis == C_y) {
set_control_state(index, events[i].value / -32767.0);
} else {
set_control_state(index, events[i].value / 32767.0);
}
control_changed(index, events[i].value);
}
}

View File

@ -290,6 +290,18 @@ init_device(const XINPUT_CAPABILITIES &caps, const XINPUT_STATE &state) {
break;
}
_controls[0]._scale = 1.0 / 255.0;
_controls[1]._scale = 1.0 / 255.0;
_controls[2]._scale = 1.0 / 32767.5;
_controls[3]._scale = 1.0 / 32767.5;
_controls[4]._scale = 1.0 / 32767.5;
_controls[5]._scale = 1.0 / 32767.5;
_controls[2]._bias = 0.5 / 32767.5;
_controls[3]._bias = 0.5 / 32767.5;
_controls[4]._bias = 0.5 / 32767.5;
_controls[5]._bias = 0.5 / 32767.5;
if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) {
set_button_map(0, ButtonHandle::none());
set_button_map(1, ButtonHandle::none());
@ -387,12 +399,12 @@ init_device(const XINPUT_CAPABILITIES &caps, const XINPUT_STATE &state) {
}
}
set_control_state(0, state.Gamepad.bLeftTrigger / 255.0);
set_control_state(1, state.Gamepad.bRightTrigger / 255.0);
set_control_state(2, state.Gamepad.sThumbLX / 32767.0);
set_control_state(3, state.Gamepad.sThumbLY / 32767.0);
set_control_state(4, state.Gamepad.sThumbRX / 32767.0);
set_control_state(5, state.Gamepad.sThumbRY / 32767.0);
control_changed(0, state.Gamepad.bLeftTrigger);
control_changed(1, state.Gamepad.bRightTrigger);
control_changed(2, state.Gamepad.sThumbLX);
control_changed(3, state.Gamepad.sThumbLY);
control_changed(4, state.Gamepad.sThumbRX);
control_changed(5, state.Gamepad.sThumbRY);
_last_buttons = buttons;
_last_packet = state.dwPacketNumber;
@ -453,7 +465,7 @@ do_poll() {
WORD mask = 1;
for (int i = 0; i < 16; ++i) {
if (changed_buttons & mask) {
set_button_state(i, (state.Gamepad.wButtons & mask) != 0);
button_changed(i, (state.Gamepad.wButtons & mask) != 0);
}
mask <<= 1;
if (i == 10) {
@ -462,12 +474,12 @@ do_poll() {
}
}
set_control_state(0, state.Gamepad.bLeftTrigger / 255.0);
set_control_state(1, state.Gamepad.bRightTrigger / 255.0);
set_control_state(2, (state.Gamepad.sThumbLX + 0.5) / 32767.5);
set_control_state(3, (state.Gamepad.sThumbLY + 0.5) / 32767.5);
set_control_state(4, (state.Gamepad.sThumbRX + 0.5) / 32767.5);
set_control_state(5, (state.Gamepad.sThumbRY + 0.5) / 32767.5);
control_changed(0, state.Gamepad.bLeftTrigger);
control_changed(1, state.Gamepad.bRightTrigger);
control_changed(2, state.Gamepad.sThumbLX);
control_changed(3, state.Gamepad.sThumbLY);
control_changed(4, state.Gamepad.sThumbRX);
control_changed(5, state.Gamepad.sThumbRY);
_last_buttons = state.Gamepad.wButtons;
_last_packet = state.dwPacketNumber;

View File

@ -103,6 +103,6 @@ vrpn_button_callback(void *userdata, const vrpn_BUTTONCB info) {
Devices::iterator di;
for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
VrpnButtonDevice *device = (*di);
device->set_button_state(info.button, info.state != 0);
device->button_changed(info.button, info.state != 0);
}
}