mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-18 01:59:19 +02:00
control messaging system, keyboard and gamepad inputs
This commit is contained in:
352
gamepadinput.cpp
Normal file
352
gamepadinput.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "gamepadinput.h"
|
||||
#include "logs.h"
|
||||
#include "timer.h"
|
||||
#include "usefull.h"
|
||||
|
||||
glm::vec2 circle_to_square( glm::vec2 const &Point, int const Roundness = 0 ) {
|
||||
|
||||
// Determine the theta angle
|
||||
auto angle = std::atan2( Point.x, Point.y ) + M_PI;
|
||||
|
||||
glm::vec2 squared;
|
||||
// Scale according to which wall we're clamping to
|
||||
// X+ wall
|
||||
if( angle <= M_PI_4 || angle > 7 * M_PI_4 )
|
||||
squared = Point * (float)( 1.0 / std::cos( angle ) );
|
||||
// Y+ wall
|
||||
else if( angle > M_PI_4 && angle <= 3 * M_PI_4 )
|
||||
squared = Point * (float)( 1.0 / std::sin( angle ) );
|
||||
// X- wall
|
||||
else if( angle > 3 * M_PI_4 && angle <= 5 * M_PI_4 )
|
||||
squared = Point * (float)( -1.0 / std::cos( angle ) );
|
||||
// Y- wall
|
||||
else if( angle > 5 * M_PI_4 && angle <= 7 * M_PI_4 )
|
||||
squared = Point * (float)( -1.0 / std::sin( angle ) );
|
||||
|
||||
// Early-out for a perfect square output
|
||||
if( Roundness == 0 )
|
||||
return squared;
|
||||
|
||||
// Find the inner-roundness scaling factor and LERP
|
||||
auto const length = glm::length( Point );
|
||||
auto const factor = std::pow( length, Roundness );
|
||||
return interpolate( Point, squared, factor );
|
||||
}
|
||||
|
||||
gamepad_input::gamepad_input() {
|
||||
|
||||
m_modecommands = {
|
||||
|
||||
{ user_command::mastercontrollerincrease, user_command::mastercontrollerdecrease },
|
||||
{ user_command::trainbrakedecrease, user_command::trainbrakeincrease },
|
||||
{ user_command::secondcontrollerincrease, user_command::secondcontrollerdecrease },
|
||||
{ user_command::independentbrakedecrease, user_command::independentbrakeincrease }
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
gamepad_input::init() {
|
||||
|
||||
// NOTE: we're only checking for joystick_1 and rely for it to stay connected throughout.
|
||||
// not exactly flexible, but for quick hack it'll do
|
||||
auto const name = glfwGetJoystickName( GLFW_JOYSTICK_1 );
|
||||
if( name != nullptr ) {
|
||||
WriteLog( "Connected gamepad: " + std::string( name ) );
|
||||
m_deviceid = GLFW_JOYSTICK_1;
|
||||
}
|
||||
else {
|
||||
// no joystick,
|
||||
WriteLog( "No gamepad detected" );
|
||||
m_axes.clear();
|
||||
m_buttons.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
int count;
|
||||
|
||||
glfwGetJoystickAxes( m_deviceid, &count );
|
||||
m_axes.resize( count );
|
||||
|
||||
glfwGetJoystickButtons( m_deviceid, &count );
|
||||
m_buttons.resize( count );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// checks state of the controls and sends issued commands
|
||||
void
|
||||
gamepad_input::poll() {
|
||||
|
||||
int count; std::size_t idx = 0;
|
||||
// poll button state
|
||||
auto const buttons = glfwGetJoystickButtons( m_deviceid, &count );
|
||||
if( count ) { // safety check in case joystick gets pulled out
|
||||
for( auto &button : m_buttons ) {
|
||||
|
||||
if( button != buttons[ idx ] ) {
|
||||
// button pressed or released, both are important
|
||||
on_button(
|
||||
static_cast<gamepad_button>( idx ),
|
||||
( buttons[ idx ] == 1 ?
|
||||
GLFW_PRESS :
|
||||
GLFW_RELEASE ) );
|
||||
}
|
||||
else {
|
||||
// otherwise we only pass info about button being held down
|
||||
if( button == 1 ) {
|
||||
|
||||
on_button(
|
||||
static_cast<gamepad_button>( idx ),
|
||||
GLFW_REPEAT );
|
||||
}
|
||||
}
|
||||
button = buttons[ idx ];
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_deviceid == -1 ) {
|
||||
// if there's no gamepad we can skip the rest
|
||||
return;
|
||||
}
|
||||
// poll axes state
|
||||
idx = 0;
|
||||
glm::vec2 leftstick, rightstick, triggers;
|
||||
auto const axes = glfwGetJoystickAxes( m_deviceid, &count );
|
||||
if( count ) {
|
||||
// safety check in case joystick gets pulled out
|
||||
if( count >= 2 ) {
|
||||
leftstick = glm::vec2(
|
||||
( std::abs( axes[ gamepad_axes::leftstick_x ] ) > m_deadzone ?
|
||||
axes[ gamepad_axes::leftstick_x ] :
|
||||
0.0f ),
|
||||
( std::abs( axes[ gamepad_axes::leftstick_y ] ) > m_deadzone ?
|
||||
axes[ gamepad_axes::leftstick_y ] :
|
||||
0.0f ) );
|
||||
}
|
||||
if( count >= 4 ) {
|
||||
rightstick = glm::vec2(
|
||||
( std::abs( axes[ gamepad_axes::rightstick_x ] ) > m_deadzone ?
|
||||
axes[ gamepad_axes::rightstick_x ] :
|
||||
0.0f ),
|
||||
( std::abs( axes[ gamepad_axes::rightstick_y ] ) > m_deadzone ?
|
||||
axes[ gamepad_axes::rightstick_y ] :
|
||||
0.0f ) );
|
||||
}
|
||||
if( count >= 6 ) {
|
||||
triggers = glm::vec2(
|
||||
( axes[ gamepad_axes::lefttrigger ] > m_deadzone ?
|
||||
axes[ gamepad_axes::lefttrigger ] :
|
||||
0.0f ),
|
||||
( axes[ gamepad_axes::righttrigger ] > m_deadzone ?
|
||||
axes[ gamepad_axes::righttrigger ] :
|
||||
0.0f ) );
|
||||
}
|
||||
}
|
||||
process_axes( leftstick, rightstick, triggers );
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_input::on_button( gamepad_button const Button, int const Action ) {
|
||||
|
||||
switch( Button ) {
|
||||
// NOTE: this is rigid coupling, down the road we should support more flexible binding of functions with buttons
|
||||
case gamepad_button::a:
|
||||
case gamepad_button::b:
|
||||
case gamepad_button::x:
|
||||
case gamepad_button::y: {
|
||||
|
||||
if( Action == GLFW_RELEASE ) {
|
||||
// if the button was released the stick controls movement
|
||||
m_mode = control_mode::entity;
|
||||
// zero the stick and the accumulator so the input won't bleed between modes
|
||||
m_leftstick = glm::vec2();
|
||||
m_modeaccumulator = 0.0f;
|
||||
}
|
||||
else {
|
||||
// otherwise set control mode to match pressed button
|
||||
m_mode = static_cast<control_mode>( Button );
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_input::process_axes( glm::vec2 Leftstick, glm::vec2 const &Rightstick, glm::vec2 const &Triggers ) {
|
||||
|
||||
// left stick, look around
|
||||
if( ( Rightstick.x != 0.0f ) || ( Rightstick.y != 0.0f ) ) {
|
||||
// TODO: make toggles for the axis flip
|
||||
auto const deltatime = Timer::GetDeltaRenderTime() * 60.0;
|
||||
double const turnx = Rightstick.x * 10.0 * deltatime;
|
||||
double const turny = -Rightstick.y * 10.0 * deltatime;
|
||||
m_relay.post(
|
||||
user_command::viewturn,
|
||||
reinterpret_cast<std::uint64_t const &>( turnx ),
|
||||
reinterpret_cast<std::uint64_t const &>( turny ),
|
||||
GLFW_PRESS,
|
||||
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
0 );
|
||||
}
|
||||
|
||||
// right stick, either movement or controls, depending on currently active mode
|
||||
if( m_mode == control_mode::entity ) {
|
||||
|
||||
if( ( Leftstick.x != 0.0 || Leftstick.y != 0.0 )
|
||||
|| ( m_leftstick.x != 0.0 || m_leftstick.y != 0.0 ) ) {
|
||||
double const movex = static_cast<double>( Leftstick.x );
|
||||
double const movez = static_cast<double>( Leftstick.y );
|
||||
m_relay.post(
|
||||
user_command::movevector,
|
||||
reinterpret_cast<std::uint64_t const &>( movex ),
|
||||
reinterpret_cast<std::uint64_t const &>( movez ),
|
||||
GLFW_PRESS,
|
||||
0 );
|
||||
}
|
||||
/*
|
||||
Leftstick = circle_to_square( Leftstick );
|
||||
if( Leftstick.y >= 0.0f ) {
|
||||
// forward
|
||||
process_axis(
|
||||
Leftstick.y, m_leftstick.y,
|
||||
1.0,
|
||||
user_command::moveforward,
|
||||
user_command::moveforwardfast,
|
||||
0 );
|
||||
}
|
||||
if( Leftstick.y <= 0.0f ) {
|
||||
// back
|
||||
process_axis(
|
||||
Leftstick.y, m_leftstick.y,
|
||||
-1.0,
|
||||
user_command::moveback,
|
||||
user_command::movebackfast,
|
||||
0 );
|
||||
}
|
||||
if( Leftstick.x >= 0.0f ) {
|
||||
// right
|
||||
process_axis(
|
||||
Leftstick.x, m_leftstick.x,
|
||||
1.0,
|
||||
user_command::moveright,
|
||||
user_command::moverightfast,
|
||||
0 );
|
||||
}
|
||||
if( Leftstick.x <= 0.0f ) {
|
||||
// left
|
||||
process_axis(
|
||||
Leftstick.x, m_leftstick.x,
|
||||
-1.0,
|
||||
user_command::moveleft,
|
||||
user_command::moveleftfast,
|
||||
0 );
|
||||
}
|
||||
*/
|
||||
}
|
||||
else {
|
||||
// vehicle control modes
|
||||
process_mode( Leftstick.y, 0 );
|
||||
}
|
||||
|
||||
m_rightstick = Rightstick;
|
||||
m_leftstick = Leftstick;
|
||||
m_triggers = Triggers;
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_input::process_axis( float const Value, float const Previousvalue, float const Multiplier, user_command Command, std::uint16_t const Recipient ) {
|
||||
|
||||
process_axis( Value, Previousvalue, Multiplier, Command, Command, Recipient );
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_input::process_axis( float const Value, float const Previousvalue, float const Multiplier, user_command Command1, user_command Command2, std::uint16_t const Recipient ) {
|
||||
|
||||
user_command command{ Command1 };
|
||||
if( Value * Multiplier > 0.9 ) {
|
||||
command = Command2;
|
||||
}
|
||||
|
||||
if( Value * Multiplier > 0.0f ) {
|
||||
|
||||
m_relay.post(
|
||||
command,
|
||||
0, 0,
|
||||
GLFW_PRESS,
|
||||
Recipient
|
||||
);
|
||||
}
|
||||
else {
|
||||
// if we had movement before but not now, report this as 'button' release
|
||||
if( Previousvalue != 0.0f ) {
|
||||
m_relay.post(
|
||||
command, // doesn't matter which movement 'mode' we report
|
||||
0, 0,
|
||||
GLFW_RELEASE,
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_input::process_mode( float const Value, std::uint16_t const Recipient ) {
|
||||
|
||||
// TODO: separate multiplier for each mode, to allow different, customizable sensitivity for each control
|
||||
auto const deltatime = Timer::GetDeltaTime() * 15.0;
|
||||
auto const &lookup = m_modecommands.at( static_cast<size_t>( m_mode ) );
|
||||
|
||||
if( Value >= 0.0f ) {
|
||||
if( m_modeaccumulator < 0.0f ) {
|
||||
// reset accumulator if we're going in the other direction i.e. issuing opposite control
|
||||
m_modeaccumulator = 0.0f;
|
||||
}
|
||||
if( Value > m_deadzone ) {
|
||||
m_modeaccumulator += Value * deltatime;
|
||||
while( m_modeaccumulator >= 1.0f ) {
|
||||
// send commands if the accumulator(s) was filled
|
||||
m_relay.post(
|
||||
lookup.first,
|
||||
0, 0,
|
||||
GLFW_PRESS,
|
||||
Recipient );
|
||||
m_modeaccumulator -= 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( m_modeaccumulator > 0.0f ) {
|
||||
// reset accumulator if we're going in the other direction i.e. issuing opposite control
|
||||
m_modeaccumulator = 0.0f;
|
||||
}
|
||||
if( Value < m_deadzone ) {
|
||||
m_modeaccumulator += Value * deltatime;
|
||||
while( m_modeaccumulator <= -1.0f ) {
|
||||
// send commands if the accumulator(s) was filled
|
||||
m_relay.post(
|
||||
lookup.second,
|
||||
0, 0,
|
||||
GLFW_PRESS,
|
||||
Recipient );
|
||||
m_modeaccumulator += 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user