RobertBeckebans / RBDOOM-3-BFG

Doom 3 BFG Edition source port with updated DX12 / Vulkan renderer and modern game engine features
https://www.moddb.com/mods/rbdoom-3-bfg
GNU General Public License v3.0
1.38k stars 247 forks source link

SDL joystick input handling implementation #209

Closed Wintermute0110 closed 9 years ago

Wintermute0110 commented 9 years ago

I did an implementation for XBox 360-like gamepads. Seems to work OK and I have tested it with both an XBox360 wireless gamepad and a Logitech F710 wireless gamepad. Implementation is SDL2 safe.

The main limitation is that SDL button/axis number are hard coded to Doom3 names. If you use a non-Xbox360 gamepad this may have unwanted results. Also, to bind button/axis to actions the files joy_lefty.cfg and joy_xbox360_0.cfg must be manually edited. I'm willing to implement a way of solving both problems if you suggest a proper way of doing it (for example, a configuration file to map SDL button/axis numbers to Doom3 names and a Doom3 menu entry for joystick remapping/binding).

This should close issue #64.

Please try it and enjoy.

Wintermute0110 commented 9 years ago

I figured out an elegant solution for a) SDL axis/button numbers hard-coded to Doom3 names, b) Binding and use of joy_lefty.cfg, joy_righty.cfg and joy_xbox360_0.cfg (which are files from the XBox360 port), c) use only of the first joystick in the system.

a) To solve this, new console variables (cVar) of the kind sys_joystick_button_A , sys_joystick_axis_LX will be created. These will have defaults for the XBox 360. These variables can be configured from a joystick.cfg file in the base directory.

b) I will ignore joy_lefty.cfg, joy_righty.cfg and joy_xbox360_0.cfg from the XBox 360 port. If the user wants to configure both a) and change bindings, he/she will modify the file joystick.cfg in the base directory. That is, joy_righty.cfg and joy_xbox360_0.cfg plus the SDL to Doom3 axis/button names will be unified into the single file joystick.cfg for exclusive use of the SDL/Linux port.

c) A console var sys_joystick_number will configure the joystick the user wants to use. Additionally, the user can use the SDL_JOYSTICK environment variable to force Doom3 to use that joystick.

In addition to this, a small test program that mimics the Doom3 joystick implementation will be provided so the user can test her joystick setup and discover which axis/button correspond to the Doom3/bindings.

If you agree with this solution I will implement it on my branch so please don't merge until finished and tested. Of course, if you figure out a better/more elegant solution please tell me and I will try to code it.

RobertBeckebans commented 9 years ago

Good work so far. I think it is good enough to support Xbox 360 gamepads only. However your implementation does not match the Win32 implementation and the buttons aren't correctly mapped according to the menu. I would like this to work without any extra .cfg files. Everything of this fork should work without any additional data files. That is the reason for baking the renderer shaders into the binary.

Please have a look at this: https://www.dropbox.com/s/2y4cfyv4kapoevs/rbdoom-3-bfg-20141125-111252-001.png?dl=0

2014-11-25 8:35 GMT+01:00 Wintermute0110 notifications@github.com:

I figured out an elegant solution for a) SDL axis/button numbers hard-coded to Doom3 names, b) Binding and use of joy_lefty.cfg, joy_righty.cfg and joy_xbox360_0.cfg (which are files from the XBox360 port), c) use only of the first joystick in the system.

a) To solve this, new console variables (cVar) of the kind sys_joystick_button_A , sys_joystick_axis_LX will be created. These will have defaults for the XBox 360. These variables can be configured from a joystick.cfg file in the base directory.

b) I will ignore joy_lefty.cfg, joy_righty.cfg and joy_xbox360_0.cfg from the XBox 360 port. If the user wants to configure both a) and change bindings, he/she will modify the file joystick.cfg in the base directory. That is, joy_righty.cfg and joy_xbox360_0.cfg plus the SDL to Doom3 axis/button names will be unified into the single file joystick.cfg for exclusive use of the SDL/Linux port.

c) A console var sys_joystick_number will configure the joystick the user wants to use. Additionally, the user can use the SDL_JOYSTICK environment variable to force Doom3 to use that joystick.

In addition to this, a small test program that mimics the Doom3 joystick implementation will be provided so the user can test her joystick setup and discover which axis/button correspond to the Doom3/bindings.

If you agree with this solution I will implement it on my branch so please don't merge until finished and tested. Of course, if you figure out a better/more elegant solution please tell me and I will try to code it.

— Reply to this email directly or view it on GitHub https://github.com/RobertBeckebans/RBDOOM-3-BFG/pull/209#issuecomment-64320523 .

RobertBeckebans commented 9 years ago

I tested it with a 360 wired controller on Kubuntu 13.10.

2014-11-25 11:29 GMT+01:00 Robert Beckebans robert.beckebans@gmail.com:

Good work so far. I think it is good enough to support Xbox 360 gamepads only. However your implementation does not match the Win32 implementation and the buttons aren't correctly mapped according to the menu. I would like this to work without any extra .cfg files. Everything of this fork should work without any additional data files. That is the reason for baking the renderer shaders into the binary.

Please have a look at this: https://www.dropbox.com/s/2y4cfyv4kapoevs/rbdoom-3-bfg-20141125-111252-001.png?dl=0

2014-11-25 8:35 GMT+01:00 Wintermute0110 notifications@github.com:

I figured out an elegant solution for a) SDL axis/button numbers hard-coded to Doom3 names, b) Binding and use of joy_lefty.cfg, joy_righty.cfg and joy_xbox360_0.cfg (which are files from the XBox360 port), c) use only of the first joystick in the system.

a) To solve this, new console variables (cVar) of the kind sys_joystick_button_A , sys_joystick_axis_LX will be created. These will have defaults for the XBox 360. These variables can be configured from a joystick.cfg file in the base directory.

b) I will ignore joy_lefty.cfg, joy_righty.cfg and joy_xbox360_0.cfg from the XBox 360 port. If the user wants to configure both a) and change bindings, he/she will modify the file joystick.cfg in the base directory. That is, joy_righty.cfg and joy_xbox360_0.cfg plus the SDL to Doom3 axis/button names will be unified into the single file joystick.cfg for exclusive use of the SDL/Linux port.

c) A console var sys_joystick_number will configure the joystick the user wants to use. Additionally, the user can use the SDL_JOYSTICK environment variable to force Doom3 to use that joystick.

In addition to this, a small test program that mimics the Doom3 joystick implementation will be provided so the user can test her joystick setup and discover which axis/button correspond to the Doom3/bindings.

If you agree with this solution I will implement it on my branch so please don't merge until finished and tested. Of course, if you figure out a better/more elegant solution please tell me and I will try to code it.

— Reply to this email directly or view it on GitHub https://github.com/RobertBeckebans/RBDOOM-3-BFG/pull/209#issuecomment-64320523 .

RobertBeckebans commented 9 years ago

In int idJoystickWin32::PollInputEvents( int inputDeviceNum ) { ... if( session->IsSystemUIShowing() ) { // memset xis so the current input does not get latched if the UI is showing memset( &xis, 0, sizeof( XINPUT_STATE ) ); }

int joyRemap[16] =
{
    J_DPAD_UP,      J_DPAD_DOWN,    // Up, Down
    J_DPAD_LEFT,    J_DPAD_RIGHT,   // Left, Right
    J_ACTION9,      J_ACTION10,     // Start, Back
    J_ACTION7,      J_ACTION8,      // Left Stick Down, Right Stick Down
    J_ACTION5,      J_ACTION6,      // Black, White (Left Shoulder, Right Shoulder)
    0,              0,              // Unused
    J_ACTION1,      J_ACTION2,      // A, B
    J_ACTION3,      J_ACTION4,      // X, Y
};

// Check the digital buttons
for( int i = 0; i < 16; i++ )
{
    int mask = ( 1 << i );
    if( ( xis.Gamepad.wButtons & mask ) != ( old.Gamepad.wButtons & mask ) )
    {
        PostInputEvent( inputDeviceNum, joyRemap[i], ( xis.Gamepad.wButtons & mask ) > 0 );
    }
}

Is is the cause of the different mapping?

Wintermute0110 commented 9 years ago

Hi Robert,

the reason SDL axis/button numbers do not match is because I did the implementation with a XBox360 wireless and you use a XBox 360 USB wired. The responsible for the different mapping is the xpad kernel driver which is notoriously bugged/outdated. The xpad driver arbitrarily assigns axis/button numbers even for identical gamepads, that's the reason why your Xbox wired mapping is incorrect with the current implementation. My XBox360 wireless is not officially supported until kernel version 3.6.18 (I am running now 3.6.16 Debian unstable stock kernel) and my wireless pad is detected as an XBox clone gamepad. Another example of bad xpad behavior: for my Xbox360 wireless SDL reports 8 axis and no hats, but for the Logitech F710, which is an identical gamepad, SDL reports 6 axis an a hat.

I understand you don't want new config files and will try to keep things as they are now. I figured out a new method for joystick mapping in Linux. Users can use xboxdrv user-space driver instead of kernel default xpad. xboxdrv allows to remap axis/buttons so any joystick can mimic an xpad Xbox360 wired gampad. I can try it and if it works as expected I will document how to remap axis/buttons for any gamepad to emulate an XBox wired gamepad easily and do a new merge request.

http://pingus.seul.org/~grumbel/xboxdrv/

Another thing: currently RBDoom3-BFG reads joy_righty.cfg and joy_xbox360_0.cfg (for some reason joy_lefty.cfg is never read, leftyFlip variable on line 437 neo/framework/PlayerProfile.cpp is never true even if you try to reverse the joystick mapping on the GUI menu).

BielBdeLuna commented 9 years ago

this is really cool! does this work with the dualskock3 or any gamepad other than xbox's? in Windows d3bfg does.

DanielGibson commented 9 years ago

I think the SDL2 Controller API is supposed to give a consistent mapping of button and axis numbers for xbox360-like gamepads. Maybe this could help?

BielBdeLuna commented 9 years ago

RBDoom3BFG do work with the Dualshock3 (or the SIXAXIS, or the PS3 gamepad) but the bindings are wrongly binded for that gamepad

I use jstest-gtk which indicates far more buttons that whatever the gamepad actually seems to use. maybe jstest-gtk allows for more buttons and axes and therefore it shows those strange numbers:

when opening the engine I get the joystick recognized as such:

Sys_InitInput: Joystick subsystem init
Sys_InitInput: Joystic - Found 1 joysticks
Opened Joystick number 0
Name: Sony PLAYSTATION(R)3 Controller
Number of Axes: 27
Number of Buttons: 19
Number of Hats: 0
Number of Balls: 0

I get the following errors messages:

WARNING: Sys_GetEvent: unknown SDL event 1541
scroll 
moveToIndex 1
moveDiff = 1
moveDiff = 1
moveDiff = 1
WARNING: Sys_GetEvent(): Unknown joystick button number 15

WARNING: Sys_GetEvent(): Unknown joystick button number 15

scroll 
moveToIndex 3
moveDiff = 1
moveDiff = 1
moveDiff = 1
WARNING: Sys_GetEvent(): Unknown joystick button number 15

WARNING: Sys_GetEvent(): Unknown joystick button number 15

Added local user: Biel-laptop
Opening IP socket: localhost:27015
execing joy_lefty.cfg
execing joy_360_0.cfg
WARNING: Sys_GetEvent(): Unknown joystick button number 15

WARNING: Sys_GetEvent(): Unknown joystick button number 15

WARNING: Sys_GetEvent(): Unknown joystick button number 15

WARNING: Sys_GetEvent(): Unknown joystick button number 15

Added local user: Biel-laptop
Opening IP socket: localhost:27015
execing joy_lefty.cfg
execing joy_360_0.cfg
Added local user: Biel-laptop
Opening IP socket: localhost:27015
execing joy_lefty.cfg
execing joy_360_0.cfg
WARNING: Sys_GetEvent(): Unknown joystick button number 15

WARNING: Sys_GetEvent(): Unknown joystick button number 15

also in vanilla doom3 there was a zoom option which was used for the ironsight mods, this option is actually there but not binded, but you can bind a script to the _zoom option and you can bind "_zoom" to a key, and use those ironsight mods in d3bfg as I have tried them and they work.

"_zoom" should be there in the joystick readme list of options

Wintermute0110 commented 9 years ago

I think the SDL2 Controller API is supposed to give a consistent mapping of button and axis numbers for xbox360-like gamepads. Maybe this could help?

I think that feature is only available if you use SDL2 in Windows thanks to DirectInput/Xinput.

Under Linux, I have tested my Xbox360 wireless (which is detected as a non-standard, clone Xbox gamepad by the outdated xpad kernel driver) with both SDL and SDL2 and the results are the same.

DanielGibson commented 9 years ago

This should work with SDL2 on Linux as well, they have mappings for several devices, see http://hg.libsdl.org/SDL/file/2cc90bb31777/src/joystick/SDL_gamecontrollerdb.h Maybe your version of SDL2 doesn't have a mapping for your controller yet?

Anyway, there is https://wiki.libsdl.org/SDL_GameControllerAddMappingsFromFile to load a mapping from a file and https://github.com/gabomdq/SDL_GameControllerDB has a file with lots of mappings.

Wintermute0110 commented 9 years ago

RBDoom3BFG do work with the Dualshock3 (or the SIXAXIS, or the PS3 gamepad) but the bindings are wrongly binded for that gamepad

I use jstest-gtk which indicates far more buttons that whatever the gamepad actually seems to use. maybe jstest-gtk allows for more buttons and axes and therefore it shows those strange numbers:

Thanks for the testing and feedback.

Having 27 axes is bizarre!!!! That's obviously a kernel gamepad driver bug.

First error you see () corresponds to SDL2 joystick hotplug events, which are not currently implemented. Error code 1541 is 0x605, which in turn is JOYDEVICEADDED event (you plugged the joystick AFTER running RBDoom3, please plug the joystick before). Anyway, this warning does not harm.

http://hg.libsdl.org/SDL/file/2cc90bb31777/include/SDL_events.h

WARNING: Sys_GetEvent(): Unknown joystick button number 15

In my implementation I only considered buttons up to 14. In a future revision I will include more buttons so people with many-button-joysticks can bind more commands.

Anyway, @BielBdeLuna you can use the xboxdrv user space driver which supports your PS3 gamepad and allows emulation of an XBox 360 gamepad. Please have a look at this

https://wiki.archlinux.org/index.php/joystick#Playstation_3_Controllers_via_USB

Setting xboxdrv can be cumbersome, specially if you want to automatically start at boot or when you plug the joystick in.

also in vanilla doom3 there was a zoom option which was used for the ironsight mods, this option is actually there but not binded, but you can bind a script to the _zoom option and you can bind "_zoom" to a key, and use those ironsight mods in d3bfg as I have tried them and they work.

"_zoom" should be there in the joystick readme list of options

Noted. Will update the documentation.


To everyone: If we convince Robert to allow the use of a file like joystick.cfg with console commands I will implement arbitrary axis/button mapping so every joystick can be used. If this file is not created by the user default XBox mapping/bindings will be used. If Robert does not approve, non Xbox gamepad owners will have to use xboxdrv, which is difficult to use (but not impossible and I will document it well once I try it with my Xbox360, Logitech F710 and MazCatz C.T.R.L. R).

Wintermute0110 commented 9 years ago

This should work with SDL2 on Linux as well, they have mappings for several devices, see http://hg.libsdl.org/SDL/file/2cc90bb31777/src/joystick/SDL_gamecontrollerdb.h Maybe your version of SDL2 doesn't have a mapping for your controller yet?

Anyway, there is https://wiki.libsdl.org/SDL_GameControllerAddMappingsFromFile to load a mapping from a file and https://github.com/gabomdq/SDL_GameControllerDB has a file with lots of mappings.

You are absolutely right. I did the SDL2 implementation quickly, just to make the SDL implementation to compile in SDL2. I will have a look at this and try to support this SDL_GameControllerAddMappingsFromFile() SDL2 feature.

BielBdeLuna commented 9 years ago

if we add a new cfg file, let's not name it "joystick.cfg" but "gamepad.cfg" it should be a more correct name

Wintermute0110 commented 9 years ago

Just a (quick) status report of last developments:

a) SDL2 controller API is very nice, but significantly more complex. If the controller/joystick is not recognized in the database then the standard joystick API is used. That means both APIs should be used in SDL2 and the code is going to be significantly bigger. @BielBdeLuna, you will be able to use your PS3 controller without problem (that gamepad is in the database).

SDL 2.0.3 includes a hard-coded small database with some gamepads. SDL 2.0.2 (version currently in Debian) does not, so the user have to download https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt and put that into Doom3 base directory. If the user gamepad is not in the database it's not clear to me at the moment how to create the game database file (which is an ASCII file), but I will figure it out and document it. Notably, my MazCatz C.T.R.L. R is not in the database.

b) I have created 2 small joystick utility programs, one for SDL and another for SDL2. Still haven't done any commit into my RBDOOM3-BFG branch. These programs allow the user to know the axis/button number mapping and also if the gamepad is recognised in SDL2.

c) SDL2 joystick database loading working OK. Thanks to @DanielGibson for providing the info.

d) Under SDL2 joystick hot-plugging will be supported.

e) If the user uses SDL1 then the user will have to set the SDL to Doom3 axis/button mapping. I'm still waiting for @RobertBeckebans about how to do this (do nothing at all (current implementation) or allow a gamepad.cfg file with console variables like "sys_sdl_axis_leftx int". This file must be created by the user and if it's not there default XBox 360 wireless mapping will be used).

Once I finish the joystick/gamepad test programs and test them thoroughly (specially hot-plug, which makes the code trickier), incorporating the code into RBDOOM3-BFG will be a piece of cake. If everything goes well I will open another pull request this weekend.

BielBdeLuna commented 9 years ago

great!

Wintermute0110 commented 9 years ago

I finished an SDL2 implementation of the gamepad API. Under SDL2 these are the new features: controller support and controller button mapping, controller/joystick hot plug and rumble/haptics.

I haven't still requested a pull because I didn't have time to do a lot of testing and to update the documentation. Also, I will include a small test program to do the mappings of any joystick for gamepad support. Please try the version in my branch and if everything works as expected I will make the pull request when the README_SDL_joystick.txt is updated and potential bugs solved.

Instructions:

a) Make sure you compile with SDL2.

b) SDL2 includes some gamepad database internally (about 10 under Linux). If you want to have more chances your joystick is detected as a gamepad, download the file

https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt

and put into your Doom 3 base directory. It will be automatically loaded if available.

c) If your gamepad is not in the database or the mapping is wrong (for example, my Logitech F710 has wrong mappings for the back, start and guide buttons, I suspect I have an early version of the hardware and the one in the database is the newer one) then you will have to do your own mapping file. It is easier than it seems. Until I finish my utility, please use this program from the SDL2 examples,

http://hg.libsdl.org/SDL/file/a11e8f6d82d1/test/controllermap.c

or you can use the SDL2 gamepad test program test_gamepad_SDL2 currently in utils/ directory. Basically, in the controller database you tell SDL2 which axis/button number corresponds with the names: X, Y, A, B, START, etc.

d) If you use SDL1 or SDL2 does not detect your joystick as a gamepad then the implementation is the same as last pull request: no SDL to Doom3 mappings as still didn't hear anything from Robert.

Once everything works OK and the documentation is updated I will make the pull request.

Enjoy!

BielBdeLuna commented 9 years ago

excellent. it works flawless for me with Ubuntu 14.10 and a PS3 game-pad.

rumble works ok

once D3 has started with the game-pad plugged, I can unplug it and plug it again and it works all-right.

in the case of a ps3 gamepad one might have to "activate" first the game-pad which is easily achieved by pressing the "ps" button in the game-pad (which is the "menu button" in the standard binding)

for me, this is a keeper!

I wonder if in the future we could add a new menu option in order to multiply the amount of rumble (a la Dolphin emulator)

BielBdeLuna commented 9 years ago

I would like to make a proposal for gamepad gameplay:

I'm quite eager to play with the game-pad related game-play stuff, I'm currently work in progress with GPL AI which could benefit the player with new functionality straight of newer input data the keyboard can't deliver. http://idtechforums.fuzzylogicinc.com/index.php?topic=133.0

I've also identified general fixes that have to be implemented for the new controls:

those fixes might not be that important within the current d3 game but for a new game (even a third person one) could be great fixes.

Wintermute0110 commented 9 years ago

@BielBdeLuna thanks for your feedback.

About rumble: rumble was designed to work with the 2 motors of the XBox pad. However, SDL rumble API only has 1 parameter for rumble strength. I did a quick and dirty mixing of these two variables to create the SDL required strength, and there could be ways to improve it... Also, for wireless gamepads rumble is a battery killer and it would be nice to allow the user to deactivate rumble in the console.

Another feature missing is that for gamepad playing _speed and _movedown should be toggled when button is pressed. Currently, if you want to run you have to continously press _speed button in the gamepad which is very inconvenient. Not sure how to code this, any suggestion?

Rest of changes: @BielBdeLuna most of the changes/improvements you propose are doom3 engine modifications rather than joystick/gamepad features. I would like to focus on proper gamepad/joystick support for Linux/SDL and once this is finished and working well then move on. But first things first.

The following days I will be quite busy with real life, so not sure when I will have time to finish off and do the pull request. In the mean time more testing and feedback would be welcomed.

BielBdeLuna commented 9 years ago

toggling crouch could both be beneficial and a problem (it might eliminate the pressure the player feels when under fire and he has to crouch down and keep pressing "_movedown" in order to not get hit, is this good or bad? I would prefer a "in_toggleCrouch" cvar to control this.)

I think "_speed" (running) is a keyboard specific functionality that might not translate that good in game-pad, but at the same time the stamina limits it's erasing as you would lose D3functionality by erasing it.

A solution I think could be achieved by the moving joystick itself. so you could go from stopped to maximum speed just with the joystick (no need for a _speed button) if you want to control the movement you push lightly the joystick (not a s a button but as a normal joystick) andyou don't run at maximum speed, but if you go all the way then you run and you start to lose stamina. And this is also it's weak point, in the beginning of the game the player might be able to control it, but once a time has passed the player would always run without stamina because fatigue of being constantly controlling the joystick.

Another solution would involve some sort of AI that would allow for a button to do two things: the jump button is useless most of the time (most of the time it allows you to bunny-hop) One jumps quite scarcely in modern games, so why not add it the functionality to "run" besides of "jumping" but then, how do we jump? this is where AI is used. the player doesn't jump per se, the AI does (just like how Assassin's Creed works) this has to be completely difficult to code tough. the biggest problem in coding this AI is that you should build the context by which the AI takes decisions, and this means analyse the player's surroundings, and take decisions based on the player direction of movement and speed. This would constitute also a loss of the ability to jump whenever one wants. maybe you could make the avatar jump if the player releases immediately the button.

BielBdeLuna commented 9 years ago

I'm working on the ironsight stuff, I would like to implement some restrictions but only for the gamepad input, like when zoomed only in the gamepad input reduce the speed of the camera's rotation by some number, yet still, set the mouse input free from this reduction.

so is there a way to identify the input and act only on specific input? I think this case should be handled within the idPlayer::think that is where the actual _zooming functionality is handled