Merge pull request #7625 from jordan-woyak/emu-shake-fix

WiimoteEmu: Shaking emulation fix
This commit is contained in:
Tilka 2018-12-27 22:32:50 +01:00 committed by GitHub
commit 89eedc8c1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 65 deletions

View File

@ -23,7 +23,7 @@ enum class ClassicGroup;
enum class GuitarGroup; enum class GuitarGroup;
enum class DrumsGroup; enum class DrumsGroup;
enum class TurntableGroup; enum class TurntableGroup;
} } // namespace WiimoteEmu
enum enum
{ {
@ -55,6 +55,9 @@ enum class InitializeMode
DO_NOT_WAIT_FOR_WIIMOTES, DO_NOT_WAIT_FOR_WIIMOTES,
}; };
// The Real Wii Remote sends report every ~5ms (200 Hz).
constexpr int UPDATE_FREQ = 200;
void Shutdown(); void Shutdown();
void Initialize(InitializeMode init_mode); void Initialize(InitializeMode init_mode);
void Connect(unsigned int index, bool connect); void Connect(unsigned int index, bool connect);
@ -78,7 +81,7 @@ void InterruptChannel(int number, u16 channel_id, const void* data, u32 size);
bool ButtonPressed(int number); bool ButtonPressed(int number);
void Update(int number, bool connected); void Update(int number, bool connected);
bool NetPlay_GetButtonPress(int wiimote, bool pressed); bool NetPlay_GetButtonPress(int wiimote, bool pressed);
} } // namespace Wiimote
namespace WiimoteReal namespace WiimoteReal
{ {
@ -90,4 +93,4 @@ void Pause();
void Refresh(); void Refresh();
void LoadSettings(); void LoadSettings();
} } // namespace WiimoteReal

View File

@ -21,26 +21,6 @@ namespace WiimoteEmu
{ {
constexpr std::array<u8, 6> classic_id{{0x00, 0x00, 0xa4, 0x20, 0x01, 0x01}}; constexpr std::array<u8, 6> classic_id{{0x00, 0x00, 0xa4, 0x20, 0x01, 0x01}};
// Classic Controller calibration
constexpr std::array<u8, 0x10> classic_calibration{{
0xff,
0x00,
0x80,
0xff,
0x00,
0x80,
0xff,
0x00,
0x80,
0xff,
0x00,
0x80,
0x00,
0x00,
0x51,
0xa6,
}};
constexpr std::array<u16, 9> classic_button_bitmasks{{ constexpr std::array<u16, 9> classic_button_bitmasks{{
Classic::BUTTON_A, Classic::BUTTON_A,
Classic::BUTTON_B, Classic::BUTTON_B,
@ -124,9 +104,35 @@ Classic::Classic(ExtensionReg& reg) : Attachment(_trans("Classic"), reg)
new ControllerEmu::Input(ControllerEmu::Translate, named_direction)); new ControllerEmu::Input(ControllerEmu::Translate, named_direction));
} }
// Set up register
m_calibration = classic_calibration;
m_id = classic_id; m_id = classic_id;
// Build calibration data:
m_calibration = {{
// Left Stick X max,min,center:
CAL_STICK_CENTER + CAL_STICK_RANGE,
CAL_STICK_CENTER - CAL_STICK_RANGE,
CAL_STICK_CENTER,
// Left Stick Y max,min,center:
CAL_STICK_CENTER + CAL_STICK_RANGE,
CAL_STICK_CENTER - CAL_STICK_RANGE,
CAL_STICK_CENTER,
// Right Stick X max,min,center:
CAL_STICK_CENTER + CAL_STICK_RANGE,
CAL_STICK_CENTER - CAL_STICK_RANGE,
CAL_STICK_CENTER,
// Right Stick Y max,min,center:
CAL_STICK_CENTER + CAL_STICK_RANGE,
CAL_STICK_CENTER - CAL_STICK_RANGE,
CAL_STICK_CENTER,
// Left/Right trigger range: (assumed based on real calibration data values)
LEFT_TRIGGER_RANGE,
RIGHT_TRIGGER_RANGE,
// 2 checksum bytes calculated below:
0x00,
0x00,
}};
UpdateCalibrationDataChecksum(m_calibration);
} }
void Classic::GetState(u8* const data) void Classic::GetState(u8* const data)
@ -213,4 +219,4 @@ ControllerEmu::ControlGroup* Classic::GetGroup(ClassicGroup group)
return nullptr; return nullptr;
} }
} }
} } // namespace WiimoteEmu

View File

@ -12,7 +12,7 @@ class AnalogStick;
class Buttons; class Buttons;
class ControlGroup; class ControlGroup;
class MixedTriggers; class MixedTriggers;
} } // namespace ControllerEmu
namespace WiimoteEmu namespace WiimoteEmu
{ {
@ -48,16 +48,25 @@ public:
PAD_UP = 0x0100, PAD_UP = 0x0100,
}; };
static const u8 LEFT_STICK_CENTER_X = 0x20; enum
static const u8 LEFT_STICK_CENTER_Y = 0x20; {
static const u8 LEFT_STICK_RADIUS = 0x1F; CAL_STICK_CENTER = 0x80,
CAL_STICK_RANGE = 0x7f,
CAL_STICK_BITS = 8,
static const u8 RIGHT_STICK_CENTER_X = 0x10; LEFT_STICK_BITS = 6,
static const u8 RIGHT_STICK_CENTER_Y = 0x10; LEFT_STICK_CENTER_X = CAL_STICK_CENTER >> (CAL_STICK_BITS - LEFT_STICK_BITS),
static const u8 RIGHT_STICK_RADIUS = 0x0F; LEFT_STICK_CENTER_Y = CAL_STICK_CENTER >> (CAL_STICK_BITS - LEFT_STICK_BITS),
LEFT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - LEFT_STICK_BITS),
static const u8 LEFT_TRIGGER_RANGE = 0x1F; RIGHT_STICK_BITS = 5,
static const u8 RIGHT_TRIGGER_RANGE = 0x1F; RIGHT_STICK_CENTER_X = CAL_STICK_CENTER >> (CAL_STICK_BITS - RIGHT_STICK_BITS),
RIGHT_STICK_CENTER_Y = CAL_STICK_CENTER >> (CAL_STICK_BITS - RIGHT_STICK_BITS),
RIGHT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - RIGHT_STICK_BITS),
LEFT_TRIGGER_RANGE = 0x1F,
RIGHT_TRIGGER_RANGE = 0x1F,
};
private: private:
ControllerEmu::Buttons* m_buttons; ControllerEmu::Buttons* m_buttons;
@ -66,4 +75,4 @@ private:
ControllerEmu::AnalogStick* m_left_stick; ControllerEmu::AnalogStick* m_left_stick;
ControllerEmu::AnalogStick* m_right_stick; ControllerEmu::AnalogStick* m_right_stick;
}; };
} } // namespace WiimoteEmu

View File

@ -69,6 +69,35 @@ Nunchuk::Nunchuk(ExtensionReg& reg) : Attachment(_trans("Nunchuk"), reg)
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
m_id = nunchuk_id; m_id = nunchuk_id;
// Build calibration data:
m_calibration = {{
// Accel Zero X,Y,Z:
ACCEL_ZERO_G,
ACCEL_ZERO_G,
ACCEL_ZERO_G,
// Possibly LSBs of zero values:
0x00,
// Accel 1G X,Y,Z:
ACCEL_ONE_G,
ACCEL_ONE_G,
ACCEL_ONE_G,
// Possibly LSBs of 1G values:
0x00,
// Stick X max,min,center:
STICK_CENTER + STICK_RADIUS,
STICK_CENTER - STICK_RADIUS,
STICK_CENTER,
// Stick Y max,min,center:
STICK_CENTER + STICK_RADIUS,
STICK_CENTER - STICK_RADIUS,
STICK_CENTER,
// 2 checksum bytes calculated below:
0x00,
0x00,
}};
UpdateCalibrationDataChecksum(m_calibration);
} }
void Nunchuk::GetState(u8* const data) void Nunchuk::GetState(u8* const data)
@ -125,9 +154,9 @@ void Nunchuk::GetState(u8* const data)
s16 accel_y = (s16)(4 * (accel.y * ACCEL_RANGE + ACCEL_ZERO_G)); s16 accel_y = (s16)(4 * (accel.y * ACCEL_RANGE + ACCEL_ZERO_G));
s16 accel_z = (s16)(4 * (accel.z * ACCEL_RANGE + ACCEL_ZERO_G)); s16 accel_z = (s16)(4 * (accel.z * ACCEL_RANGE + ACCEL_ZERO_G));
accel_x = MathUtil::Clamp<s16>(accel_x, 0, 1024); accel_x = MathUtil::Clamp<s16>(accel_x, 0, 0x3ff);
accel_y = MathUtil::Clamp<s16>(accel_y, 0, 1024); accel_y = MathUtil::Clamp<s16>(accel_y, 0, 0x3ff);
accel_z = MathUtil::Clamp<s16>(accel_z, 0, 1024); accel_z = MathUtil::Clamp<s16>(accel_z, 0, 0x3ff);
nc_data.ax = (accel_x >> 2) & 0xFF; nc_data.ax = (accel_x >> 2) & 0xFF;
nc_data.ay = (accel_y >> 2) & 0xFF; nc_data.ay = (accel_y >> 2) & 0xFF;
@ -186,4 +215,4 @@ void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
m_buttons->SetControlExpression(1, "Shift_L"); // Z m_buttons->SetControlExpression(1, "Shift_L"); // Z
#endif #endif
} }
} } // namespace WiimoteEmu

View File

@ -14,7 +14,7 @@ class Buttons;
class ControlGroup; class ControlGroup;
class Force; class Force;
class Tilt; class Tilt;
} } // namespace ControllerEmu
namespace WiimoteEmu namespace WiimoteEmu
{ {
@ -69,4 +69,4 @@ private:
std::array<u8, 3> m_shake_soft_step{}; std::array<u8, 3> m_shake_soft_step{};
std::array<u8, 3> m_shake_hard_step{}; std::array<u8, 3> m_shake_hard_step{};
}; };
} } // namespace WiimoteEmu

View File

@ -11,6 +11,7 @@
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <mutex> #include <mutex>
#include <numeric>
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -56,6 +57,11 @@ auto const PI = TAU / 2.0;
namespace WiimoteEmu namespace WiimoteEmu
{ {
constexpr int SHAKE_FREQ = 6;
// Frame count of one up/down shake
// < 9 no shake detection in "Wario Land: Shake It"
constexpr int SHAKE_STEP_MAX = ::Wiimote::UPDATE_FREQ / SHAKE_FREQ;
// clang-format off // clang-format off
static const u8 eeprom_data_0[] = { static const u8 eeprom_data_0[] = {
// IR, maybe more // IR, maybe more
@ -101,13 +107,20 @@ static const ReportFeatures reporting_mode_features[] = {
{0, 0, 0, 0, 23}, {0, 0, 0, 0, 23},
}; };
// Used for extension calibration data:
void UpdateCalibrationDataChecksum(std::array<u8, 0x10>& data)
{
// Last two bytes are the sum of the previous bytes plus 0x55 and 0xaa (0x55 + 0x55)
const u8 checksum1 = std::accumulate(std::begin(data), std::end(data) - 2, u8(0x55));
const u8 checksum2 = checksum1 + 0x55;
data[0xe] = checksum1;
data[0xf] = checksum2;
}
void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_group, void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_group,
const double intensity, u8* const shake_step) const double intensity, u8* const shake_step)
{ {
// frame count of one up/down shake
// < 9 no shake detection in "Wario Land: Shake It"
auto const shake_step_max = 15;
// shake is a bitfield of X,Y,Z shake button states // shake is a bitfield of X,Y,Z shake button states
static const unsigned int btns[] = {0x01, 0x02, 0x04}; static const unsigned int btns[] = {0x01, 0x02, 0x04};
unsigned int shake = 0; unsigned int shake = 0;
@ -117,8 +130,8 @@ void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_
{ {
if (shake & (1 << i)) if (shake & (1 << i))
{ {
(&(accel->x))[i] = std::sin(TAU * shake_step[i] / shake_step_max) * intensity; (&(accel->x))[i] += std::sin(TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity;
shake_step[i] = (shake_step[i] + 1) % shake_step_max; shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX;
} }
else else
shake_step[i] = 0; shake_step[i] = 0;
@ -129,10 +142,6 @@ void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data,
ControllerEmu::Buttons* const buttons_group, ControllerEmu::Buttons* const buttons_group,
const DynamicConfiguration& config, u8* const shake_step) const DynamicConfiguration& config, u8* const shake_step)
{ {
// frame count of one up/down shake
// < 9 no shake detection in "Wario Land: Shake It"
auto const shake_step_max = 15;
// shake is a bitfield of X,Y,Z shake button states // shake is a bitfield of X,Y,Z shake button states
static const unsigned int btns[] = {0x01, 0x02, 0x04}; static const unsigned int btns[] = {0x01, 0x02, 0x04};
unsigned int shake = 0; unsigned int shake = 0;
@ -146,8 +155,9 @@ void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data,
} }
else if (dynamic_data.executing_frames_left[i] > 0) else if (dynamic_data.executing_frames_left[i] > 0)
{ {
(&(accel->x))[i] = std::sin(TAU * shake_step[i] / shake_step_max) * dynamic_data.intensity[i]; (&(accel->x))[i] +=
shake_step[i] = (shake_step[i] + 1) % shake_step_max; std::sin(TAU * shake_step[i] / SHAKE_STEP_MAX) * dynamic_data.intensity[i];
shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX;
dynamic_data.executing_frames_left[i]--; dynamic_data.executing_frames_left[i]--;
} }
else if (shake == 0 && dynamic_data.timing[i] > 0) else if (shake == 0 && dynamic_data.timing[i] > 0)
@ -651,9 +661,9 @@ void Wiimote::GetAccelData(u8* const data, const ReportFeatures& rptf)
s16 y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G)); s16 y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G));
s16 z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G)); s16 z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G));
x = MathUtil::Clamp<s16>(x, 0, 1024); x = MathUtil::Clamp<s16>(x, 0, 0x3ff);
y = MathUtil::Clamp<s16>(y, 0, 1024); y = MathUtil::Clamp<s16>(y, 0, 0x3ff);
z = MathUtil::Clamp<s16>(z, 0, 1024); z = MathUtil::Clamp<s16>(z, 0, 0x3ff);
accel.x = (x >> 2) & 0xFF; accel.x = (x >> 2) & 0xFF;
accel.y = (y >> 2) & 0xFF; accel.y = (y >> 2) & 0xFF;
@ -1025,11 +1035,15 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
// Buttons // Buttons
#if defined HAVE_X11 && HAVE_X11 #if defined HAVE_X11 && HAVE_X11
m_buttons->SetControlExpression(0, "Click 1"); // A // A
m_buttons->SetControlExpression(1, "Click 3"); // B m_buttons->SetControlExpression(0, "Click 1");
// B
m_buttons->SetControlExpression(1, "Click 3");
#else #else
m_buttons->SetControlExpression(0, "Click 0"); // A // A
m_buttons->SetControlExpression(1, "Click 1"); // B m_buttons->SetControlExpression(0, "Click 0");
// B
m_buttons->SetControlExpression(1, "Click 1");
#endif #endif
m_buttons->SetControlExpression(2, "1"); // 1 m_buttons->SetControlExpression(2, "1"); // 1
m_buttons->SetControlExpression(3, "2"); // 2 m_buttons->SetControlExpression(3, "2"); // 2

View File

@ -34,7 +34,7 @@ class ModifySettingsButton;
class NumericSetting; class NumericSetting;
class Output; class Output;
class Tilt; class Tilt;
} } // namespace ControllerEmu
namespace WiimoteReal namespace WiimoteReal
{ {
@ -181,6 +181,8 @@ struct ExtensionReg
}; };
#pragma pack(pop) #pragma pack(pop)
void UpdateCalibrationDataChecksum(std::array<u8, 0x10>& data);
void EmulateShake(AccelData* accel, ControllerEmu::Buttons* buttons_group, double intensity, void EmulateShake(AccelData* accel, ControllerEmu::Buttons* buttons_group, double intensity,
u8* shake_step); u8* shake_step);
@ -377,4 +379,4 @@ private:
#pragma pack(pop) #pragma pack(pop)
}; };
} } // namespace WiimoteEmu

View File

@ -356,8 +356,7 @@ void BluetoothEmu::Update()
} }
} }
// The Real Wii Remote sends report every ~5ms (200 Hz). const u64 interval = SystemTimers::GetTicksPerSecond() / Wiimote::UPDATE_FREQ;
const u64 interval = SystemTimers::GetTicksPerSecond() / 200;
const u64 now = CoreTiming::GetTicks(); const u64 now = CoreTiming::GetTicks();
if (now - m_last_ticks > interval) if (now - m_last_ticks > interval)