Files
firestorm/Gameleap/code/mw4/Libraries/Adept/Joystick.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

835 lines
20 KiB
C++

#include "AdeptHeaders.hpp"
#include "Joystick.hpp"
//############################################################################
//############################### Input Filters ##############################
//############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construct a Unipolar input filter
//
UnipolarFilter::UnipolarFilter(
Scalar minimum,
Scalar maximum,
Scalar dead_band,
Direction direction,
LimitType limit_type
):
inputAverage(5,0.0f)
{
positiveDirection = direction;
Calibrate(minimum, maximum, dead_band);
limitType = limit_type;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
UnipolarFilter::~UnipolarFilter()
{
Check_Object(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
UnipolarFilter::Update(Scalar value)
{
Check_Object(this);
//
//------------------------------------------------
// Generate average, use for min/max determination
//------------------------------------------------
//
switch (limitType)
{
case PanningLimits:
{
inputAverage.Add(value);
Scalar average = inputAverage.CalculateOlympicAverage();
if (average < inputMin)
{
inputMax -= inputMin - average;
inputMin = average;
}
else if (average > inputMax)
{
inputMin += average - inputMax;
inputMax = average;
}
}
break;
case FloatingLimits:
{
inputAverage.Add(value);
Scalar average = inputAverage.CalculateOlympicAverage();
if (average < inputMin)
{
Calibrate(average, inputMax, deadBand);
}
else if (average > inputMax)
{
Calibrate(inputMin, average, deadBand);
}
}
break;
}
//
//-------------------------------------------------------------------------
// In the case of a unipolar filter, subtract out the minimum value and the
// deadband, the scale the remainder base on the range
//-------------------------------------------------------------------------
//
Scalar result = 0.0f;
value -= inputMin + lowerDeadband;
if (value>0 && lowerRange>0)
{
if (value > lowerRange)
{
result = 1.0f;
}
else
{
result = static_cast<Scalar>(value)/lowerRange;
Verify(result >= 0.0f && result <= 1.0f);
}
}
if (positiveDirection == DecreasingPositive)
{
result = 1.0f - result;
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Recalibrates the filter
//
void
UnipolarFilter::Calibrate(
Scalar minimum,
Scalar maximum,
Scalar dead_band
)
{
Check_Object(this);
Verify(dead_band >= 0.0f);
Verify(maximum >= minimum);
deadBand = dead_band;
inputMin = minimum;
inputMax = maximum;
lowerDeadband = (inputMax-inputMin)*deadBand + 0.5f;
lowerRange = (inputMax-lowerDeadband) - (inputMin+lowerDeadband);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
UnipolarFilter::BeginAlignment()
{
Check_Object(this);
Scalar average = inputAverage.CalculateAverage();
Calibrate(average, average, deadBand);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
UnipolarFilter::EndAlignment()
{
Check_Object(this);
//
//----------------------------------------------------------
// Move the edges of the filter ranges back by three percent
//----------------------------------------------------------
//
Scalar delta = (inputMax - inputMin) * 0.03f;
inputMax -= delta;
inputMin += delta;
Calibrate(inputMin, inputMax, deadBand);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
UnipolarFilter::SetDeadBand(Scalar percent)
{
Check_Object(this);
Calibrate(inputMin, inputMax, percent);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construct a Bipolar input filter
//
BipolarFilter::BipolarFilter(
Scalar minimum,
Scalar center,
Scalar maximum,
Scalar dead_band,
Direction direction,
LimitType limit_type
):
UnipolarFilter(minimum, maximum, dead_band, direction, limit_type)
{
Verify(limit_type != PanningLimits);
Calibrate(minimum, center, maximum, dead_band);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BipolarFilter::~BipolarFilter()
{
Check_Object(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
BipolarFilter::Update(Scalar value)
{
Check_Object(this);
//
//------------------------------------------------
// Generate average, use for min/max determination
//------------------------------------------------
//
switch (limitType)
{
case FloatingLimits:
{
inputAverage.Add(value);
Scalar average = inputAverage.CalculateOlympicAverage();
if (average < inputMin)
{
Calibrate(average, inputCenter, inputMax, deadBand);
}
else if (average > inputMax)
{
Calibrate(inputMin, inputCenter, average, deadBand);
}
}
break;
}
//
//-----------------------------------------------------------
// Figure out which half of the input we will be dealing with
//-----------------------------------------------------------
//
Scalar result = 0.0f;
value -= inputCenter;
//
//------------------------------------------------------------------
// If we are in the lower half of the input and beyond the deadband,
// scale the input against the lower half range
//------------------------------------------------------------------
//
if (value < 0)
{
value += lowerDeadband;
if (value<0 && lowerRange>=0)
{
if (value <= -lowerRange)
{
result = -1.0f;
}
else
{
result = value/lowerRange;
Verify(result >= -1.0f && result < 0.0f);
}
}
}
//
//--------------------------------------------------------------------
// Otherwise, we are in the upper half of the input. If we are beyond
// the deadband, scale the input against the upper half range
//--------------------------------------------------------------------
//
else
{
value -= upperDeadband;
if (value>0 && upperRange>=0)
{
if (value >= upperRange)
{
result = 1.0f;
}
else
{
result = value/upperRange;
Verify(result >= 0.0f && result <= 1.0f);
}
}
}
if (positiveDirection == DecreasingPositive)
{
result = -result;
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Recalibrates the filter
//
void
BipolarFilter::Calibrate(
Scalar minimum,
Scalar center,
Scalar maximum,
Scalar dead_band
)
{
Check_Object(this);
Verify(dead_band >= 0.0f);
Verify(minimum <= maximum);
Verify(center >= minimum);
Verify(center <= maximum);
deadBand = dead_band;
inputMin = minimum;
inputCenter = center;
inputMax = maximum;
lowerDeadband = (inputCenter-inputMin)*deadBand;
lowerRange = center - (inputMin+lowerDeadband);
upperDeadband = (inputMax-inputCenter)*deadBand;
upperRange = (inputMax-upperDeadband) - inputCenter;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BipolarFilter::BeginAlignment()
{
Check_Object(this);
Scalar average = inputAverage.CalculateAverage();
Calibrate(average, average, average, deadBand);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BipolarFilter::EndAlignment()
{
Check_Object(this);
//
//---------------------------------------------------------------------
// Calculate the new center point by the current joystick average, then
// move the edges of the joystick ranges back by three percent
//---------------------------------------------------------------------
//
Scalar average = inputAverage.CalculateAverage();
inputMax -= (inputMax - average) * 0.03f;
inputMin += (average - inputMin) * 0.03f;
Calibrate(inputMin, average, inputMax, deadBand);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BipolarFilter::SetDeadBand(Scalar percent)
{
Check_Object(this);
Calibrate(inputMin, inputCenter, inputMax, percent);
}
//###########################################################################
//############################ Joystick classes #############################
//###########################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joystick::Joystick(
DWORD stick_id,
Scalar x_dead_band,
Scalar y_dead_band,
Scalar throttle_dead_band,
Scalar rudder_dead_band
)
{
Check_Pointer(this);
buttonCount = CalculateButtonCount(stick_id);
joystickID = stick_id;
m_ShiftButton = -1;
//
//--------------------------------
// Initialize the member variables
//--------------------------------
//
hatValue = -1.0f;
lastHatValue = -1.0f;
hatStatus = NoHat;
//
//------------------------------
// Build the filterpointer array
//------------------------------
//
memset(axisValues,0,sizeof(axisValues));
memset(axisFilters,0,sizeof(axisFilters));
//
//---------------------------------------------------
// Set up the filters for X and Y, which we will have
//---------------------------------------------------
//
gosJoystick_Info ji;
gosJoystick_GetInfo(stick_id, &ji);
m_ForceFeedback = ji.bIsForceFeedback;
Verify(ji.bAxisValid & (1<<JOY_XAXIS));
joystickXInput = JOY_XAXIS;
UnipolarFilter *filter =
new BipolarFilter(
-1.0f,
0.0f,
1.0f,
x_dead_band,
BipolarFilter::IncreasingPositive,
BipolarFilter::FixedLimits
);
AdoptFilter(XAxis, filter);
Verify(ji.bAxisValid & (1<<JOY_YAXIS));
joystickYInput = JOY_YAXIS;
filter =
new BipolarFilter(
-1.0f,
0.0f,
1.0f,
y_dead_band,
BipolarFilter::IncreasingPositive,
BipolarFilter::FixedLimits
);
AdoptFilter(YAxis, filter);
//
//------------------
// Deal with the hat
//------------------
//
if (ji.nPOVs > 0)
{
hatStatus = HasAHat;
}
//
//------------------------
// Add the throttle device
//------------------------
//
if (ji.bAxisValid & (1<<JOY_THROTTLE))
{
throttleInput = JOY_THROTTLE;
filter =
new ThrottleFilter(
-1.0f,
1.0f,
throttle_dead_band
);
AdoptFilter(ThrottleAxis, filter);
}
else
if (ji.bAxisValid & (1<<JOY_SLIDER1))
{
throttleInput = JOY_SLIDER1;
filter =
new ThrottleFilter(
-1.0f,
1.0f,
throttle_dead_band
);
AdoptFilter(ThrottleAxis, filter);
}
else
{
throttleInput = static_cast<GOSJoystickAxis>(12); //ELEMENTS(ji.bAxisValid));
}
//
//----------------------
// Add the rudder device
//----------------------
//
if (ji.bAxisValid & (1<<JOY_RUDDER))
{
rudderInput = JOY_RUDDER;
filter =
new BipolarFilter(
-1.0f,
0.0f,
1.0f,
rudder_dead_band,
BipolarFilter::IncreasingPositive,
BipolarFilter::FixedLimits
);
AdoptFilter(RudderAxis, filter);
}
else
{
rudderInput = static_cast<GOSJoystickAxis>(12); //ELEMENTS(ji.bAxisValid));
}
//
//-------------------------------
// Read the initial button states
//-------------------------------
//
buttonValues = new bool[buttonCount];
Register_Pointer(buttonValues);
for (DWORD i=0; i<buttonCount; ++i)
{
buttonValues[i] = gosJoystick_ButtonStatus(joystickID, i);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joystick::~Joystick()
{
Check_Object(this);
//
//------------------------------
// Delete any additional filters
//------------------------------
//
Verify(axisFilters);
int i;
for (i=0; i<AxisCount; ++i)
{
if (axisFilters[i])
{
Unregister_Object(axisFilters[i]);
delete axisFilters[i];
}
}
Unregister_Pointer(buttonValues);
delete [] buttonValues;
gosJoystick_SetPolling(joystickID, false);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::FilterInput(
int axis,
Scalar value
)
{
Check_Object(this);
Verify(static_cast<unsigned>(axis) < AxisCount);
Check_Object(axisFilters[axis]);
axisValues[axis] = axisFilters[axis]->Update(value);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::AdoptFilter(
int axis,
UnipolarFilter *filter
)
{
Check_Object(this);
Verify(static_cast<unsigned>(axis) < AxisCount);
Register_Object(filter);
axisFilters[axis] = filter;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::SetDeadBand(Scalar percent)
{
Check_Object(this);
for (int i=0; i<AxisCount; ++i)
{
if (axisFilters[i])
{
Check_Object(axisFilters[i]);
axisFilters[i]->SetDeadBand(percent);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::SetXDeadBand(Scalar percent)
{
Check_Object(this);
if (axisFilters[XAxis])
{
Check_Object(axisFilters[XAxis]);
axisFilters[XAxis]->SetDeadBand(percent);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::SetYDeadBand(Scalar percent)
{
Check_Object(this);
if (axisFilters[YAxis])
{
Check_Object(axisFilters[YAxis]);
axisFilters[YAxis]->SetDeadBand(percent);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::SetThrottleDeadBand(Scalar percent)
{
Check_Object(this);
if (axisFilters[ThrottleAxis])
{
Check_Object(axisFilters[ThrottleAxis]);
axisFilters[ThrottleAxis]->SetDeadBand(percent);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::SetRudderDeadBand(Scalar percent)
{
Check_Object(this);
if (axisFilters[RudderAxis])
{
Check_Object(axisFilters[RudderAxis]);
axisFilters[RudderAxis]->SetDeadBand(percent);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Joystick::Update(
int mode_mask,
ControlsUpdateManagerOf<Scalar> *joystick_axis_group,
ControlsUpdateManagerOf<int> *joystick_button_group,
ControlsUpdateManagerOf<int> *joystick_hat
)
{
Check_Object(this);
Check_Pointer(joystick_axis_group);
Check_Pointer(joystick_button_group);
//
//----------------------------------
// First update the joystick buttons
//----------------------------------
//
Check_Pointer(buttonValues);
for (DWORD i=0; i<buttonCount; ++i)
{
if (i == m_ShiftButton) // button m_ShiftButton is the shift button on the joystick, nothing should be mapped to it.
continue;
int button = ControlsManager::JoystickButtonIDBase + i;
Check_Object(&joystick_button_group[i]);
bool pressed = gosJoystick_ButtonStatus(joystickID, i);
// if (pressed != buttonValues[i])
{
if (pressed)
{
// button = -button;
// joystick_button_group[i].Update(&button, ControlsManager::AlwaysActive);
// button = -button;
joystick_button_group[i].Update(&button, mode_mask);
}
else
{
button = -button;
joystick_button_group[i].Update(&button, ControlsManager::AlwaysActive);
}
buttonValues[i] = pressed;
}
}
//
//----------------------------------
// Update the standard joystick axes
//----------------------------------
//
Verify(static_cast<unsigned>(joystickXInput) <= JOY_SLIDER2);
FilterInput(XAxis, gosJoystick_GetAxis(joystickID, joystickXInput));
Check_Object(&joystick_axis_group[XAxis]);
joystick_axis_group[XAxis].Update(&axisValues[XAxis], 1);
Verify(static_cast<unsigned>(joystickYInput) <= JOY_SLIDER2);
FilterInput(YAxis, gosJoystick_GetAxis(joystickID, joystickYInput));
Check_Object(&joystick_axis_group[YAxis]);
joystick_axis_group[YAxis].Update(&axisValues[YAxis], 1);
//
//------------------
// Read the throttle
//------------------
//
if (static_cast<unsigned>(throttleInput) <= JOY_SLIDER2)
{
FilterInput(
ThrottleAxis,
gosJoystick_GetAxis(joystickID, throttleInput)
);
Check_Object(&joystick_axis_group[ThrottleAxis]);
joystick_axis_group[ThrottleAxis].Update(
&axisValues[ThrottleAxis],
1
);
}
//
//----------------
// Read the rudder
//----------------
//
if (static_cast<unsigned>(rudderInput) <= JOY_SLIDER2)
{
FilterInput(
RudderAxis,
gosJoystick_GetAxis(joystickID, rudderInput)
);
Check_Object(&joystick_axis_group[RudderAxis]);
joystick_axis_group[RudderAxis].Update(
&axisValues[RudderAxis],
1
);
}
//
//----------------------
// Read the joystick hat
//----------------------
//
if (HasHat())
{
axisValues[HatAxis] = gosJoystick_GetAxis(joystickID, JOY_HAT1);
Check_Object(&joystick_axis_group[HatAxis]);
joystick_axis_group[HatAxis].Update(
&axisValues[HatAxis],
1
);
hatValue = axisValues[HatAxis];
if (hatValue != lastHatValue) {
Check_Object(joystick_hat);
int temp = 1;
if (hatValue == -1.0f) {}
// Left down
else if ((hatValue>= 0.625f) && (hatValue <= 0.875f))
joystick_hat[0].Update(&temp, 1);
// Right down
else if ((hatValue>= 0.125f) && (hatValue <= 0.375f))
joystick_hat[1].Update(&temp, 1);
// Bottom down
else if ((hatValue> 0.375f) && (hatValue < 0.625f))
joystick_hat[3].Update(&temp, 1);
// Top down
else if ((hatValue > 0.875f) || (hatValue < 0.125f))
joystick_hat[2].Update(&temp, 1);
temp = 0;
if (lastHatValue == -1.0f) {}
// Left up
else if ((lastHatValue>= 0.625f) && (lastHatValue <= 0.875f))
joystick_hat[0].Update(&temp, 1);
// Right up
else if ((lastHatValue>= 0.125f) && (lastHatValue <= 0.375f))
joystick_hat[1].Update(&temp, 1);
// Bottom up
else if ((lastHatValue> 0.375f) && (lastHatValue < 0.625f))
joystick_hat[3].Update(&temp, 1);
// Top up
else if ((lastHatValue > 0.875f) || (lastHatValue < 0.125f))
joystick_hat[2].Update(&temp, 1);
lastHatValue = hatValue;
}
}
else
{
hatValue = -1.0f;
}
#if 0
//
//---------------------------------------------------
// Check to see which button values have been pressed
//---------------------------------------------------
//
int state = buttonsPressed;
int button;
for (i=0; state; ++i, state>>=1)
{
if (state&1)
{
button = ControlsManager::JoystickButtonIDBase + i;
Check_Pointer(joystick_button_group);
Check_Object(&joystick_button_group[i]);
joystick_button_group[i].Update(&button, mode_mask);
}
}
//
//----------------------------------------------------
// Check to see which button values have been released
//----------------------------------------------------
//
state = buttonsReleased;
for (i=0; state; ++i, state>>=1)
{
if (state&1)
{
button = -ControlsManager::JoystickButtonIDBase - i;
Check_Pointer(joystick_button_group);
Check_Object(&joystick_button_group[i]);
joystick_button_group[i].Update(&button, mode_mask);
}
}
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
Joystick::CalculateButtonCount(DWORD stick_id)
{
gosJoystick_Info ji;
gosJoystick_GetInfo(stick_id, &ji);
return ji.nButtons;
}
bool Joystick::ShiftButton (void)
{
if (m_ShiftButton == -1)
return false;
if (buttonCount <= m_ShiftButton)
return false;
return gosJoystick_ButtonStatus(joystickID, m_ShiftButton);
}