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

View File

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

View File

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

View File

@ -114,6 +114,45 @@ get_pointer_events() {
return result; 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. * 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._xpos += x;
_pointer_data._ypos += y; _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) { if (_enable_pointer_events) {
int seq = _event_sequence++; int seq = _event_sequence++;
if (_pointer_events.is_null()) { if (_pointer_events.is_null()) {
@ -184,7 +228,7 @@ pointer_moved(double x, double y, double time) {
* while this call is made. * while this call is made.
*/ */
void InputDevice:: void InputDevice::
set_button_state(int index, bool down) { button_changed(int index, bool down) {
nassertv(_lock.debug_is_locked()); nassertv(_lock.debug_is_locked());
nassertv(index >= 0); nassertv(index >= 0);
if (index >= (int)_buttons.size()) { if (index >= (int)_buttons.size()) {
@ -244,6 +288,34 @@ set_control_state(int index, double state) {
_controls[index].known = true; _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. * Records that a tracker movement has taken place.
*/ */
@ -467,6 +539,9 @@ operator << (ostream &out, InputDevice::DeviceClass dc) {
case InputDevice::DC_hmd: case InputDevice::DC_hmd:
out << "hmd"; out << "hmd";
break; break;
case InputDevice::DC_COUNT:
break;
} }
return out; return out;
} }
@ -518,6 +593,10 @@ operator << (ostream &out, InputDevice::ControlAxis axis) {
out << "throttle"; out << "throttle";
break; break;
case InputDevice::C_twist:
out << "twist";
break;
case InputDevice::C_rudder: case InputDevice::C_rudder:
out << "rudder"; out << "rudder";
break; break;

View File

@ -108,6 +108,7 @@ PUBLISHED:
C_y, C_y,
C_trigger, C_trigger,
C_throttle, C_throttle,
C_twist,
C_rudder, C_rudder,
C_hat_x, C_hat_x,
C_hat_y, C_hat_y,
@ -183,11 +184,18 @@ PUBLISHED:
virtual void output(ostream &out) const; virtual void output(ostream &out) const;
protected: 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(bool inwin, double x, double y, double time);
void set_pointer_out_of_window(double time); void set_pointer_out_of_window(double time);
void pointer_moved(double x, double y, 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_control_state(int index, double state);
void set_tracker(const LPoint3 &pos, const LOrientation &orient, double time); void set_tracker(const LPoint3 &pos, const LOrientation &orient, double time);
virtual void do_set_vibration(double low, double high); virtual void do_set_vibration(double low, double high);
@ -271,6 +279,10 @@ PUBLISHED:
ControlAxis axis; ControlAxis axis;
double state; double state;
bool known; bool known;
public:
double _scale;
double _bias;
}; };
typedef pvector<AnalogState> Controls; typedef pvector<AnalogState> Controls;
Controls _controls; Controls _controls;

View File

@ -322,6 +322,18 @@ open_device() {
break; break;
} }
_controls[i].axis = axis; _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; int index = events[i].number;
if (events[i].type & JS_EVENT_BUTTON) { 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) { } else if (events[i].type & JS_EVENT_AXIS) {
if (index == _dpad_x_axis) { if (index == _dpad_x_axis) {
set_button_state(_dpad_left_button, events[i].value < -1000); button_changed(_dpad_left_button, events[i].value < -1000);
set_button_state(_dpad_left_button+1, events[i].value > 1000); button_changed(_dpad_left_button+1, events[i].value > 1000);
} else if (index == _dpad_y_axis) { } else if (index == _dpad_y_axis) {
set_button_state(_dpad_up_button, events[i].value < -1000); button_changed(_dpad_up_button, events[i].value < -1000);
set_button_state(_dpad_up_button+1, events[i].value > 1000); button_changed(_dpad_up_button+1, events[i].value > 1000);
} }
ControlAxis axis = _controls[index].axis; control_changed(index, events[i].value);
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);
}
} }
} }

View File

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

View File

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