Skip to content

Commit

Permalink
InputCommon: Add real Wii Remote support to ControllerInterface. Add …
Browse files Browse the repository at this point in the history
…option to connect additional Wii Remotes.
  • Loading branch information
jordan-woyak committed Feb 17, 2020
1 parent 4176cc7 commit 58448d7
Show file tree
Hide file tree
Showing 20 changed files with 2,267 additions and 236 deletions.
2 changes: 2 additions & 0 deletions Source/Core/Core/ConfigManager.cpp
Expand Up @@ -234,6 +234,7 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("WiiKeyboard", m_WiiKeyboard);
core->Set("WiimoteContinuousScanning", m_WiimoteContinuousScanning);
core->Set("WiimoteEnableSpeaker", m_WiimoteEnableSpeaker);
core->Set("WiimoteControllerInterface", connect_wiimotes_for_ciface);
core->Set("RunCompareServer", bRunCompareServer);
core->Set("RunCompareClient", bRunCompareClient);
core->Set("MMU", bMMU);
Expand Down Expand Up @@ -511,6 +512,7 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("WiiKeyboard", &m_WiiKeyboard, false);
core->Get("WiimoteContinuousScanning", &m_WiimoteContinuousScanning, false);
core->Get("WiimoteEnableSpeaker", &m_WiimoteEnableSpeaker, false);
core->Get("WiimoteControllerInterface", &connect_wiimotes_for_ciface, false);
core->Get("RunCompareServer", &bRunCompareServer, false);
core->Get("RunCompareClient", &bRunCompareClient, false);
core->Get("MMU", &bMMU, bMMU);
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/ConfigManager.h
Expand Up @@ -72,6 +72,7 @@ struct SConfig
bool m_WiiKeyboard;
bool m_WiimoteContinuousScanning;
bool m_WiimoteEnableSpeaker;
bool connect_wiimotes_for_ciface;

// ISO folder
std::vector<std::string> m_ISOFolder;
Expand Down
22 changes: 7 additions & 15 deletions Source/Core/Core/HW/WiimoteEmu/Camera.cpp
Expand Up @@ -60,14 +60,6 @@ void CameraLogic::Update(const Common::Matrix44& transform)
using Common::Vec3;
using Common::Vec4;

constexpr int CAMERA_WIDTH = 1024;
constexpr int CAMERA_HEIGHT = 768;

// Wiibrew claims the camera FOV is about 33 deg by 23 deg.
// Unconfirmed but it seems to work well enough.
constexpr int CAMERA_FOV_X_DEG = 33;
constexpr int CAMERA_FOV_Y_DEG = 23;

constexpr auto CAMERA_FOV_Y = float(CAMERA_FOV_Y_DEG * MathUtil::TAU / 360);
constexpr auto CAMERA_ASPECT_RATIO = float(CAMERA_FOV_X_DEG) / CAMERA_FOV_Y_DEG;

Expand Down Expand Up @@ -112,12 +104,12 @@ void CameraLogic::Update(const Common::Matrix44& transform)
if (point.z > 0)
{
// FYI: Casting down vs. rounding seems to produce more symmetrical output.
const auto x = s32((1 - point.x / point.w) * CAMERA_WIDTH / 2);
const auto y = s32((1 - point.y / point.w) * CAMERA_HEIGHT / 2);
const auto x = s32((1 - point.x / point.w) * CAMERA_RES_X / 2);
const auto y = s32((1 - point.y / point.w) * CAMERA_RES_Y / 2);

const auto point_size = std::lround(MAX_POINT_SIZE / point.w / 2);

if (x >= 0 && y >= 0 && x < CAMERA_WIDTH && y < CAMERA_HEIGHT)
if (x >= 0 && y >= 0 && x < CAMERA_RES_X && y < CAMERA_RES_Y)
return CameraPoint{u16(x), u16(y), u8(point_size)};
}

Expand Down Expand Up @@ -165,7 +157,7 @@ void CameraLogic::Update(const Common::Matrix44& transform)
for (std::size_t i = 0; i != camera_points.size(); ++i)
{
const auto& p = camera_points[i];
if (p.x < CAMERA_WIDTH)
if (p.x < CAMERA_RES_X)
{
IRExtended irdata = {};

Expand All @@ -186,7 +178,7 @@ void CameraLogic::Update(const Common::Matrix44& transform)
for (std::size_t i = 0; i != camera_points.size(); ++i)
{
const auto& p = camera_points[i];
if (p.x < CAMERA_WIDTH)
if (p.x < CAMERA_RES_X)
{
IRFull irdata = {};

Expand All @@ -203,8 +195,8 @@ void CameraLogic::Update(const Common::Matrix44& transform)

irdata.xmin = std::max(p.x - p.size, 0);
irdata.ymin = std::max(p.y - p.size, 0);
irdata.xmax = std::min(p.x + p.size, CAMERA_WIDTH);
irdata.ymax = std::min(p.y + p.size, CAMERA_HEIGHT);
irdata.xmax = std::min(p.x + p.size, CAMERA_RES_X);
irdata.ymax = std::min(p.y + p.size, CAMERA_RES_Y);

// TODO: Is this maybe MSbs of the "intensity" value?
irdata.zero = 0;
Expand Down
13 changes: 13 additions & 0 deletions Source/Core/Core/HW/WiimoteEmu/Camera.h
Expand Up @@ -20,6 +20,8 @@ namespace WiimoteEmu
// Four bytes for two objects. Filled with 0xFF if empty
struct IRBasic
{
using IRObject = Common::TVec2<u16>;

u8 x1;
u8 y1;
u8 x2hi : 2;
Expand All @@ -28,6 +30,9 @@ struct IRBasic
u8 y1hi : 2;
u8 x2;
u8 y2;

auto GetObject1() const { return IRObject(x1hi << 8 | x1, y1hi << 8 | y1); }
auto GetObject2() const { return IRObject(x2hi << 8 | x2, y2hi << 8 | y2); }
};
static_assert(sizeof(IRBasic) == 5, "Wrong size");

Expand Down Expand Up @@ -62,6 +67,14 @@ static_assert(sizeof(IRFull) == 9, "Wrong size");
class CameraLogic : public I2CSlave
{
public:
static constexpr int CAMERA_RES_X = 1024;
static constexpr int CAMERA_RES_Y = 768;

// Wiibrew claims the camera FOV is about 33 deg by 23 deg.
// Unconfirmed but it seems to work well enough.
static constexpr int CAMERA_FOV_X_DEG = 33;
static constexpr int CAMERA_FOV_Y_DEG = 23;

enum : u8
{
IR_MODE_BASIC = 1,
Expand Down
61 changes: 40 additions & 21 deletions Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp
Expand Up @@ -54,11 +54,15 @@ double CalculateStopDistance(double velocity, double max_accel)
return velocity * velocity / (2 * std::copysign(max_accel, velocity));
}

// Note that 'gyroscope' is rotation of world around device.
Common::Matrix33 ComplementaryFilter(const Common::Vec3& accelerometer,
const Common::Matrix33& gyroscope, float accel_weight)
} // namespace

namespace WiimoteEmu
{
const auto gyro_vec = gyroscope * Common::Vec3{0, 0, 1};
Common::Matrix33 ComplementaryFilter(const Common::Matrix33& gyroscope,
const Common::Vec3& accelerometer, float accel_weight,
const Common::Vec3& accelerometer_normal)
{
const auto gyro_vec = gyroscope * accelerometer_normal;
const auto normalized_accel = accelerometer.Normalized();

const auto cos_angle = normalized_accel.Dot(gyro_vec);
Expand All @@ -76,10 +80,6 @@ Common::Matrix33 ComplementaryFilter(const Common::Vec3& accelerometer,
}
}

} // namespace

namespace WiimoteEmu
{
IMUCursorState::IMUCursorState() : rotation{Common::Matrix33::Identity()}
{
}
Expand Down Expand Up @@ -203,17 +203,17 @@ void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float t
}
}

WiimoteCommon::DataReportBuilder::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g,
u16 one_g)
WiimoteCommon::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g, u16 one_g)
{
const auto scaled_accel = accel * (one_g - zero_g) / float(GRAVITY_ACCELERATION);

// 10-bit integers.
constexpr long MAX_VALUE = (1 << 10) - 1;

return {u16(std::clamp(std::lround(scaled_accel.x + zero_g), 0l, MAX_VALUE)),
u16(std::clamp(std::lround(scaled_accel.y + zero_g), 0l, MAX_VALUE)),
u16(std::clamp(std::lround(scaled_accel.z + zero_g), 0l, MAX_VALUE))};
return WiimoteCommon::AccelData(
{u16(std::clamp(std::lround(scaled_accel.x + zero_g), 0l, MAX_VALUE)),
u16(std::clamp(std::lround(scaled_accel.y + zero_g), 0l, MAX_VALUE)),
u16(std::clamp(std::lround(scaled_accel.z + zero_g), 0l, MAX_VALUE))});
}

void EmulateCursor(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed)
Expand Down Expand Up @@ -311,28 +311,24 @@ void EmulateIMUCursor(IMUCursorState* state, ControllerEmu::IMUCursor* imu_ir_gr
}

// Apply rotation from gyro data.
const auto gyro_rotation = Common::Matrix33::FromQuaternion(ang_vel->x * time_elapsed / -2,
ang_vel->y * time_elapsed / -2,
ang_vel->z * time_elapsed / -2, 1);
const auto gyro_rotation = GetMatrixFromGyroscope(*ang_vel * -1 * time_elapsed);
state->rotation = gyro_rotation * state->rotation;

// If we have some non-zero accel data use it to adjust gyro drift.
constexpr auto ACCEL_WEIGHT = 0.02f;
auto const accel = imu_accelerometer_group->GetState().value_or(Common::Vec3{});
if (accel.LengthSquared())
state->rotation = ComplementaryFilter(accel, state->rotation, ACCEL_WEIGHT);

const auto inv_rotation = state->rotation.Inverted();
state->rotation = ComplementaryFilter(state->rotation, accel, ACCEL_WEIGHT);

// Clamp yaw within configured bounds.
const auto yaw = std::asin((inv_rotation * Common::Vec3{0, 1, 0}).x);
const auto yaw = GetYaw(state->rotation);
const auto max_yaw = float(imu_ir_group->GetTotalYaw() / 2);
auto target_yaw = std::clamp(yaw, -max_yaw, max_yaw);

// Handle the "Recenter" button being pressed.
if (imu_ir_group->controls[0]->GetState<bool>())
{
state->recentered_pitch = std::asin((inv_rotation * Common::Vec3{0, 1, 0}).z);
state->recentered_pitch = GetPitch(state->rotation);
target_yaw = 0;
}

Expand Down Expand Up @@ -390,10 +386,33 @@ Common::Matrix33 GetMatrixFromAcceleration(const Common::Vec3& accel)
axis.LengthSquared() ? axis.Normalized() : Common::Vec3{0, 1, 0});
}

Common::Matrix33 GetMatrixFromGyroscope(const Common::Vec3& gyro)
{
return Common::Matrix33::FromQuaternion(gyro.x / 2, gyro.y / 2, gyro.z / 2, 1);
}

Common::Matrix33 GetRotationalMatrix(const Common::Vec3& angle)
{
return Common::Matrix33::RotateZ(angle.z) * Common::Matrix33::RotateY(angle.y) *
Common::Matrix33::RotateX(angle.x);
}

float GetPitch(const Common::Matrix33& world_rotation)
{
const auto vec = world_rotation * Common::Vec3{0, 0, 1};
return std::atan2(vec.y, Common::Vec2(vec.x, vec.z).Length());
}

float GetRoll(const Common::Matrix33& world_rotation)
{
const auto vec = world_rotation * Common::Vec3{0, 0, 1};
return std::atan2(vec.x, vec.z);
}

float GetYaw(const Common::Matrix33& world_rotation)
{
const auto vec = world_rotation.Inverted() * Common::Vec3{0, 1, 0};
return std::atan2(vec.x, vec.y);
}

} // namespace WiimoteEmu
17 changes: 15 additions & 2 deletions Source/Core/Core/HW/WiimoteEmu/Dynamics.h
Expand Up @@ -54,12 +54,26 @@ struct MotionState : PositionalState, RotationalState
{
};

// Note that 'gyroscope' is rotation of world around device.
// Alternative accelerometer_normal can be supplied to correct from non-accelerometer data.
// e.g. Used for yaw/pitch correction with IR data.
Common::Matrix33 ComplementaryFilter(const Common::Matrix33& gyroscope,
const Common::Vec3& accelerometer, float accel_weight,
const Common::Vec3& accelerometer_normal = {0, 0, 1});

// Estimate orientation from accelerometer data.
Common::Matrix33 GetMatrixFromAcceleration(const Common::Vec3& accel);

// Get a rotation matrix from current gyro data.
Common::Matrix33 GetMatrixFromGyroscope(const Common::Vec3& gyro);

// Build a rotational matrix from euler angles.
Common::Matrix33 GetRotationalMatrix(const Common::Vec3& angle);

float GetPitch(const Common::Matrix33& world_rotation);
float GetRoll(const Common::Matrix33& world_rotation);
float GetYaw(const Common::Matrix33& world_rotation);

void ApproachPositionWithJerk(PositionalState* state, const Common::Vec3& target,
const Common::Vec3& max_jerk, float time_elapsed);

Expand All @@ -75,7 +89,6 @@ void EmulateIMUCursor(IMUCursorState* state, ControllerEmu::IMUCursor* imu_ir_gr
ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed);

// Convert m/s/s acceleration data to the format used by Wiimote/Nunchuk (10-bit unsigned integers).
WiimoteCommon::DataReportBuilder::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g,
u16 one_g);
WiimoteCommon::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g, u16 one_g);

} // namespace WiimoteEmu
4 changes: 0 additions & 4 deletions Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
Expand Up @@ -236,10 +236,6 @@ void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&)
// Update status struct
m_status.extension = m_extension_port.IsDeviceConnected();

// Based on testing, old WiiLi.org docs, and WiiUse library:
// Max battery level seems to be 0xc8 (decimal 200)
constexpr u8 MAX_BATTERY_LEVEL = 0xc8;

m_status.battery = u8(std::lround(m_battery_setting.GetValue() / 100 * MAX_BATTERY_LEVEL));

if (Core::WantsDeterminism())
Expand Down
17 changes: 10 additions & 7 deletions Source/Core/Core/HW/WiimoteReal/IOdarwin.mm
Expand Up @@ -257,12 +257,13 @@ @interface ConnectBT : NSObject
@implementation SearchBT
- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
error:(IOReturn)error
aborted:(BOOL)aborted {
aborted:(BOOL)aborted
{
done = true;
}

- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
device:(IOBluetoothDevice*)device {
- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender device:(IOBluetoothDevice*)device
{
NOTICE_LOG(WIIMOTE, "Discovered Bluetooth device at %s: %s", [[device addressString] UTF8String],
[[device name] UTF8String]);

Expand All @@ -274,11 +275,12 @@ - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
@implementation ConnectBT
- (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel
data:(unsigned char*)data
length:(NSUInteger)length {
length:(NSUInteger)length
{
IOBluetoothDevice* device = [l2capChannel device];
WiimoteReal::WiimoteDarwin* wm = nullptr;

std::lock_guard<std::mutex> lk(WiimoteReal::g_wiimotes_mutex);
std::lock_guard lk(WiimoteReal::g_wiimotes_mutex);

for (int i = 0; i < MAX_WIIMOTES; i++)
{
Expand Down Expand Up @@ -314,11 +316,12 @@ - (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel
CFRunLoopStop(CFRunLoopGetCurrent());
}

- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel {
- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel
{
IOBluetoothDevice* device = [l2capChannel device];
WiimoteReal::WiimoteDarwin* wm = nullptr;

std::lock_guard<std::mutex> lk(WiimoteReal::g_wiimotes_mutex);
std::lock_guard lk(WiimoteReal::g_wiimotes_mutex);

for (int i = 0; i < MAX_WIIMOTES; i++)
{
Expand Down

0 comments on commit 58448d7

Please sign in to comment.