diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h index 538718f3aa..7a1b65f587 100644 --- a/Source/Core/Common/BitUtils.h +++ b/Source/Core/Common/BitUtils.h @@ -300,6 +300,12 @@ void SetBit(T& value, size_t bit_number, bool bit_value) value &= ~(T{1} << bit_number); } +template +void SetBit(T& value, bool bit_value) +{ + SetBit(value, bit_number, bit_value); +} + template class FlagBit { @@ -340,4 +346,15 @@ public: std::underlying_type_t m_hex = 0; }; +// Left-shift a value and set new LSBs to that of the supplied LSB. +// Converts a value from a N-bit range to an (N+X)-bit range. e.g. 0x101 -> 0x10111 +template +T ExpandValue(T value, size_t left_shift_amount) +{ + static_assert(std::is_unsigned(), "ExpandValue is only sane on unsigned types."); + + return (value << left_shift_amount) | + (T(-ExtractBit<0>(value)) >> (BitSize() - left_shift_amount)); +} + } // namespace Common diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h index 2611880ee9..3f497b1f88 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h @@ -11,6 +11,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/IniFile.h" #include "InputCommon/ControlReference/ExpressionParser.h" @@ -27,6 +28,106 @@ namespace ControllerEmu { class ControlGroup; +// Represents calibration data found on Wii Remotes + extensions with a zero and a max value. +// (e.g. accelerometer data) +// Bits of precision specified to handle common situation of differing precision in the actual data. +template +struct TwoPointCalibration +{ + TwoPointCalibration() = default; + TwoPointCalibration(const T& zero_, const T& max_) : zero{zero_}, max{max_} {} + + static constexpr size_t BITS_OF_PRECISION = Bits; + + T zero; + T max; +}; + +// Represents calibration data with a min, zero, and max value. (e.g. joystick data) +template +struct ThreePointCalibration +{ + ThreePointCalibration() = default; + ThreePointCalibration(const T& min_, const T& zero_, const T& max_) + : min{min_}, zero{zero_}, max{max_} + { + } + + static constexpr size_t BITS_OF_PRECISION = Bits; + + T min; + T zero; + T max; +}; + +// Represents a raw/uncalibrated N-dimensional value of input data. (e.g. Joystick X and Y) +// A normalized value can be calculated with a provided {Two,Three}PointCalibration. +// Values are adjusted with mismatched bits of precision. +// Underlying type may be an unsigned type or a a Common::TVecN<> of an unsigned type. +template +struct RawValue +{ + RawValue() = default; + explicit RawValue(const T& value_) : value{value_} {} + + static constexpr size_t BITS_OF_PRECISION = Bits; + + T value; + + template + auto GetNormalizedValue(const TwoPointCalibration& calibration) const + { + const auto value_expansion = + std::max(0, int(calibration.BITS_OF_PRECISION) - int(BITS_OF_PRECISION)); + + const auto calibration_expansion = + std::max(0, int(BITS_OF_PRECISION) - int(calibration.BITS_OF_PRECISION)); + + const auto calibration_zero = ExpandValue(calibration.zero, calibration_expansion) * 1.f; + const auto calibration_max = ExpandValue(calibration.max, calibration_expansion) * 1.f; + + // Multiplication by 1.f to floatify either a scalar or a Vec. + return (ExpandValue(value, value_expansion) * 1.f - calibration_zero) / + (calibration_max - calibration_zero); + } + + template + auto GetNormalizedValue(const ThreePointCalibration& calibration) const + { + const auto value_expansion = + std::max(0, int(calibration.BITS_OF_PRECISION) - int(BITS_OF_PRECISION)); + + const auto calibration_expansion = + std::max(0, int(BITS_OF_PRECISION) - int(calibration.BITS_OF_PRECISION)); + + const auto calibration_min = ExpandValue(calibration.min, calibration_expansion) * 1.f; + const auto calibration_zero = ExpandValue(calibration.zero, calibration_expansion) * 1.f; + const auto calibration_max = ExpandValue(calibration.max, calibration_expansion) * 1.f; + + const auto use_max = calibration.zero < value; + + // Multiplication by 1.f to floatify either a scalar or a Vec. + return (ExpandValue(value, value_expansion) * 1.f - calibration_zero) / + (use_max * 1.f * (calibration_max - calibration_zero) + + !use_max * 1.f * (calibration_zero - calibration_min)); + } + + template + static OtherT ExpandValue(OtherT value, size_t bits) + { + if constexpr (std::is_arithmetic_v) + { + return Common::ExpandValue(value, bits); + } + else + { + for (size_t i = 0; i != std::size(value.data); ++i) + value.data[i] = Common::ExpandValue(value.data[i], bits); + return value; + } + } +}; + class EmulatedController { public: