Merge pull request #12492 from AdmiralCurtiss/wiimote-ir-passthrough

Implement IR passthrough for emulated Wiimotes
This commit is contained in:
Admiral H. Curtiss 2024-03-12 03:27:07 +01:00 committed by GitHub
commit 72bcdadc16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 242 additions and 25 deletions

View File

@ -7,6 +7,19 @@ Buttons/2 = `2`
Buttons/- = `-` Buttons/- = `-`
Buttons/+ = `+` Buttons/+ = `+`
Buttons/Home = `HOME` Buttons/Home = `HOME`
IRPassthrough/Enabled = False
IRPassthrough/Object 1 X = `IR Object 1 X`
IRPassthrough/Object 1 Y = `IR Object 1 Y`
IRPassthrough/Object 1 Size = `IR Object 1 Size`
IRPassthrough/Object 2 X = `IR Object 2 X`
IRPassthrough/Object 2 Y = `IR Object 2 Y`
IRPassthrough/Object 2 Size = `IR Object 2 Size`
IRPassthrough/Object 3 X = `IR Object 3 X`
IRPassthrough/Object 3 Y = `IR Object 3 Y`
IRPassthrough/Object 3 Size = `IR Object 3 Size`
IRPassthrough/Object 4 X = `IR Object 4 X`
IRPassthrough/Object 4 Y = `IR Object 4 Y`
IRPassthrough/Object 4 Size = `IR Object 4 Size`
IMUAccelerometer/Up = `Accel Up` IMUAccelerometer/Up = `Accel Up`
IMUAccelerometer/Down = `Accel Down` IMUAccelerometer/Down = `Accel Down`
IMUAccelerometer/Left = `Accel Left` IMUAccelerometer/Left = `Accel Left`

View File

@ -125,13 +125,14 @@ struct NoExt : virtual DataReportManipulator
u8* GetExtDataPtr() override { return nullptr; } u8* GetExtDataPtr() override { return nullptr; }
}; };
template <u32 Offset, u32 Length, u32 DataOffset = 0> template <IRReportFormat Format, u32 Offset, u32 Length, u32 DataOffset = 0>
struct IncludeIR : virtual DataReportManipulator struct IncludeIR : virtual DataReportManipulator
{ {
u32 GetIRDataSize() const override { return Length; } u32 GetIRDataSize() const override { return Length; }
const u8* GetIRDataPtr() const override { return data_ptr + Offset; } const u8* GetIRDataPtr() const override { return data_ptr + Offset; }
u8* GetIRDataPtr() override { return data_ptr + Offset; } u8* GetIRDataPtr() override { return data_ptr + Offset; }
u32 GetIRDataFormatOffset() const override { return DataOffset; } u32 GetIRDataFormatOffset() const override { return DataOffset; }
IRReportFormat GetIRReportFormat() const override { return Format; }
}; };
struct NoIR : virtual DataReportManipulator struct NoIR : virtual DataReportManipulator
@ -140,6 +141,7 @@ struct NoIR : virtual DataReportManipulator
const u8* GetIRDataPtr() const override { return nullptr; } const u8* GetIRDataPtr() const override { return nullptr; }
u8* GetIRDataPtr() override { return nullptr; } u8* GetIRDataPtr() override { return nullptr; }
u32 GetIRDataFormatOffset() const override { return 0; } u32 GetIRDataFormatOffset() const override { return 0; }
IRReportFormat GetIRReportFormat() const override { return IRReportFormat::None; }
}; };
#ifdef _MSC_VER #ifdef _MSC_VER
@ -162,7 +164,10 @@ struct ReportCoreExt8 : IncludeCore, NoAccel, NoIR, IncludeExt<2, 8>
{ {
}; };
struct ReportCoreAccelIR12 : IncludeCore, IncludeAccel, IncludeIR<5, 12>, NoExt struct ReportCoreAccelIR12 : IncludeCore,
IncludeAccel,
IncludeIR<IRReportFormat::Extended, 5, 12>,
NoExt
{ {
u32 GetDataSize() const override { return 17; } u32 GetDataSize() const override { return 17; }
}; };
@ -175,11 +180,17 @@ struct ReportCoreAccelExt16 : IncludeCore, IncludeAccel, NoIR, IncludeExt<5, 16>
{ {
}; };
struct ReportCoreIR10Ext9 : IncludeCore, NoAccel, IncludeIR<2, 10>, IncludeExt<12, 9> struct ReportCoreIR10Ext9 : IncludeCore,
NoAccel,
IncludeIR<IRReportFormat::Basic, 2, 10>,
IncludeExt<12, 9>
{ {
}; };
struct ReportCoreAccelIR10Ext6 : IncludeCore, IncludeAccel, IncludeIR<5, 10>, IncludeExt<15, 6> struct ReportCoreAccelIR10Ext6 : IncludeCore,
IncludeAccel,
IncludeIR<IRReportFormat::Basic, 5, 10>,
IncludeExt<15, 6>
{ {
}; };
@ -187,7 +198,7 @@ struct ReportExt21 : NoCore, NoAccel, NoIR, IncludeExt<0, 21>
{ {
}; };
struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt struct ReportInterleave1 : IncludeCore, IncludeIR<IRReportFormat::Full1, 3, 18, 0>, NoExt
{ {
// FYI: Only 8-bits of precision in this report, and no Y axis. // FYI: Only 8-bits of precision in this report, and no Y axis.
void GetAccelData(AccelData* accel) const override void GetAccelData(AccelData* accel) const override
@ -220,7 +231,7 @@ struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt
u32 GetDataSize() const override { return 21; } u32 GetDataSize() const override { return 21; }
}; };
struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt struct ReportInterleave2 : IncludeCore, IncludeIR<IRReportFormat::Full2, 3, 18, 18>, NoExt
{ {
// FYI: Only 8-bits of precision in this report, and no X axis. // FYI: Only 8-bits of precision in this report, and no X axis.
void GetAccelData(AccelData* accel) const override void GetAccelData(AccelData* accel) const override
@ -372,6 +383,11 @@ u32 DataReportBuilder::GetIRDataFormatOffset() const
return m_manip->GetIRDataFormatOffset(); return m_manip->GetIRDataFormatOffset();
} }
IRReportFormat DataReportBuilder::GetIRReportFormat() const
{
return m_manip->GetIRReportFormat();
}
void DataReportBuilder::GetCoreData(CoreData* core) const void DataReportBuilder::GetCoreData(CoreData* core) const
{ {
m_manip->GetCoreData(core); m_manip->GetCoreData(core);

View File

@ -39,6 +39,7 @@ public:
virtual const u8* GetIRDataPtr() const = 0; virtual const u8* GetIRDataPtr() const = 0;
virtual u32 GetIRDataSize() const = 0; virtual u32 GetIRDataSize() const = 0;
virtual u32 GetIRDataFormatOffset() const = 0; virtual u32 GetIRDataFormatOffset() const = 0;
virtual IRReportFormat GetIRReportFormat() const = 0;
virtual u8* GetExtDataPtr() = 0; virtual u8* GetExtDataPtr() = 0;
virtual const u8* GetExtDataPtr() const = 0; virtual const u8* GetExtDataPtr() const = 0;
@ -76,6 +77,7 @@ public:
u32 GetExtDataSize() const; u32 GetExtDataSize() const;
u32 GetIRDataFormatOffset() const; u32 GetIRDataFormatOffset() const;
IRReportFormat GetIRReportFormat() const;
void GetCoreData(CoreData*) const; void GetCoreData(CoreData*) const;
void GetAccelData(AccelData*) const; void GetAccelData(AccelData*) const;

View File

@ -50,6 +50,15 @@ enum class OutputReportID : u8
IRLogicEnable2 = 0x1a, IRLogicEnable2 = 0x1a,
}; };
enum class IRReportFormat : u8
{
None,
Basic, // from ReportCoreIR10Ext9 or ReportCoreAccelIR10Ext6
Extended, // from ReportCoreAccelIR12
Full1, // from ReportInterleave1
Full2, // from ReportInterleave2
};
enum class LED : u8 enum class LED : u8
{ {
None = 0x00, None = 0x00,

View File

@ -59,7 +59,7 @@ CameraLogic::GetCameraPoints(const Common::Matrix44& transform, Common::Vec2 fie
using Common::Vec3; using Common::Vec3;
using Common::Vec4; using Common::Vec4;
const std::array<Vec3, NUM_POINTS> leds{ const std::array<Vec3, 2> leds{
Vec3{-SENSOR_BAR_LED_SEPARATION / 2, 0, 0}, Vec3{-SENSOR_BAR_LED_SEPARATION / 2, 0, 0},
Vec3{SENSOR_BAR_LED_SEPARATION / 2, 0, 0}, Vec3{SENSOR_BAR_LED_SEPARATION / 2, 0, 0},
}; };
@ -68,7 +68,7 @@ CameraLogic::GetCameraPoints(const Common::Matrix44& transform, Common::Vec2 fie
Matrix44::Perspective(field_of_view.y, field_of_view.x / field_of_view.y, 0.001f, 1000) * Matrix44::Perspective(field_of_view.y, field_of_view.x / field_of_view.y, 0.001f, 1000) *
Matrix44::FromMatrix33(Matrix33::RotateX(float(MathUtil::TAU / 4))) * transform; Matrix44::FromMatrix33(Matrix33::RotateX(float(MathUtil::TAU / 4))) * transform;
std::array<CameraPoint, leds.size()> camera_points; std::array<CameraPoint, CameraLogic::NUM_POINTS> camera_points;
std::transform(leds.begin(), leds.end(), camera_points.begin(), [&](const Vec3& v) { std::transform(leds.begin(), leds.end(), camera_points.begin(), [&](const Vec3& v) {
const auto point = camera_view * Vec4(v, 1.0); const auto point = camera_view * Vec4(v, 1.0);

View File

@ -126,7 +126,7 @@ public:
// FYI: A real wiimote normally only returns 1 point for each LED cluster (2 total). // FYI: A real wiimote normally only returns 1 point for each LED cluster (2 total).
// Sending all 4 points can actually cause some stuttering issues. // Sending all 4 points can actually cause some stuttering issues.
static constexpr int NUM_POINTS = 2; static constexpr int NUM_POINTS = 4;
// Range from 0-15. Small values (2-4) seem to be very typical. // Range from 0-15. Small values (2-4) seem to be very typical.
// This is reduced based on distance from sensor bar. // This is reduced based on distance from sensor bar.

View File

@ -82,7 +82,7 @@ SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state)
if (has_camera) if (has_camera)
{ {
for (size_t i = 0; i < 2; ++i) for (size_t i = 0; i < state.camera_points.size(); ++i)
{ {
const u16 camera_x = state.camera_points[i].position.x; // 10 bits const u16 camera_x = state.camera_points[i].position.x; // 10 bits
const u16 camera_y = state.camera_points[i].position.y; // 10 bits const u16 camera_y = state.camera_points[i].position.y; // 10 bits
@ -178,7 +178,7 @@ bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimote
else if (has_accel) else if (has_accel)
s += 4; s += 4;
if (has_camera) if (has_camera)
s += 6; s += 12;
if (has_motion_plus) if (has_motion_plus)
s += 6; s += 6;
switch (extension) switch (extension)
@ -260,7 +260,7 @@ bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimote
if (has_camera) if (has_camera)
{ {
for (size_t i = 0; i < 2; ++i) for (size_t i = 0; i < state->camera_points.size(); ++i)
{ {
const u8 camera_misc = d[pos]; const u8 camera_misc = d[pos];
const u8 camera_x_high = d[pos + 1]; const u8 camera_x_high = d[pos + 1];

View File

@ -21,11 +21,12 @@ struct DesiredWiimoteState
{Wiimote::ACCEL_ZERO_G << 2, Wiimote::ACCEL_ZERO_G << 2, Wiimote::ACCEL_ONE_G << 2}); {Wiimote::ACCEL_ZERO_G << 2, Wiimote::ACCEL_ZERO_G << 2, Wiimote::ACCEL_ONE_G << 2});
// No light detected by the IR camera. // No light detected by the IR camera.
static constexpr std::array<CameraPoint, 2> DEFAULT_CAMERA = {CameraPoint(), CameraPoint()}; static constexpr std::array<CameraPoint, 4> DEFAULT_CAMERA = {CameraPoint(), CameraPoint(),
CameraPoint(), CameraPoint()};
WiimoteCommon::ButtonData buttons{}; // non-button state in this is ignored WiimoteCommon::ButtonData buttons{}; // non-button state in this is ignored
WiimoteCommon::AccelData acceleration = DEFAULT_ACCELERATION; WiimoteCommon::AccelData acceleration = DEFAULT_ACCELERATION;
std::array<CameraPoint, 2> camera_points = DEFAULT_CAMERA; std::array<CameraPoint, 4> camera_points = DEFAULT_CAMERA;
std::optional<MotionPlus::DataFormat::Data> motion_plus = std::nullopt; std::optional<MotionPlus::DataFormat::Data> motion_plus = std::nullopt;
DesiredExtensionState extension; DesiredExtensionState extension;
}; };
@ -34,7 +35,7 @@ struct DesiredWiimoteState
struct SerializedWiimoteState struct SerializedWiimoteState
{ {
u8 length; u8 length;
std::array<u8, 24> data; // 12 bytes Wiimote, 6 bytes MotionPlus, 6 bytes Extension std::array<u8, 30> data; // 18 bytes Wiimote, 6 bytes MotionPlus, 6 bytes Extension
}; };
SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state); SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state);

View File

@ -49,6 +49,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h" #include "InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h" #include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h" #include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/IRPassthrough.h"
#include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h" #include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h" #include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
@ -250,6 +251,8 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i
_trans("Camera field of view (affects sensitivity of pointing).")}, _trans("Camera field of view (affects sensitivity of pointing).")},
fov_default.y, 0.01, 180); fov_default.y, 0.01, 180);
groups.emplace_back(m_ir_passthrough = new ControllerEmu::IRPassthrough(
IR_PASSTHROUGH_GROUP, _trans("Point (Passthrough)")));
groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer( groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer(
ACCELEROMETER_GROUP, _trans("Accelerometer"))); ACCELEROMETER_GROUP, _trans("Accelerometer")));
groups.emplace_back(m_imu_gyroscope = groups.emplace_back(m_imu_gyroscope =
@ -360,6 +363,8 @@ ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group) const
return m_imu_gyroscope; return m_imu_gyroscope;
case WiimoteGroup::IMUPoint: case WiimoteGroup::IMUPoint:
return m_imu_ir; return m_imu_ir;
case WiimoteGroup::IRPassthrough:
return m_ir_passthrough;
default: default:
ASSERT(false); ASSERT(false);
return nullptr; return nullptr;
@ -447,6 +452,33 @@ void Wiimote::UpdateButtonsStatus(const DesiredWiimoteState& target_state)
m_status.buttons.hex = target_state.buttons.hex & ButtonData::BUTTON_MASK; m_status.buttons.hex = target_state.buttons.hex & ButtonData::BUTTON_MASK;
} }
static std::array<CameraPoint, CameraLogic::NUM_POINTS>
GetPassthroughCameraPoints(ControllerEmu::IRPassthrough* ir_passthrough)
{
std::array<CameraPoint, CameraLogic::NUM_POINTS> camera_points;
for (size_t i = 0; i < camera_points.size(); ++i)
{
const ControlState size = ir_passthrough->GetObjectSize(i);
if (size <= 0.0f)
continue;
const ControlState x = ir_passthrough->GetObjectPositionX(i);
const ControlState y = ir_passthrough->GetObjectPositionY(i);
camera_points[i].position.x =
std::clamp(std::lround(x * ControlState(CameraLogic::CAMERA_RES_X - 1)), long(0),
long(CameraLogic::CAMERA_RES_X - 1));
camera_points[i].position.y =
std::clamp(std::lround(y * ControlState(CameraLogic::CAMERA_RES_Y - 1)), long(0),
long(CameraLogic::CAMERA_RES_Y - 1));
camera_points[i].size =
std::clamp(std::lround(size * ControlState(CameraLogic::MAX_POINT_SIZE)), long(0),
long(CameraLogic::MAX_POINT_SIZE));
}
return camera_points;
}
void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state, void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
SensorBarState sensor_bar_state) SensorBarState sensor_bar_state)
{ {
@ -470,7 +502,11 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2); ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
// Calculate IR camera state. // Calculate IR camera state.
if (sensor_bar_state == SensorBarState::Enabled) if (m_ir_passthrough->enabled)
{
target_state->camera_points = GetPassthroughCameraPoints(m_ir_passthrough);
}
else if (sensor_bar_state == SensorBarState::Enabled)
{ {
target_state->camera_points = CameraLogic::GetCameraPoints( target_state->camera_points = CameraLogic::GetCameraPoints(
GetTotalTransformation(), GetTotalTransformation(),
@ -762,6 +798,12 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
m_imu_gyroscope->SetControlExpression(3, "`Gyro Roll Right`"); m_imu_gyroscope->SetControlExpression(3, "`Gyro Roll Right`");
m_imu_gyroscope->SetControlExpression(4, "`Gyro Yaw Left`"); m_imu_gyroscope->SetControlExpression(4, "`Gyro Yaw Left`");
m_imu_gyroscope->SetControlExpression(5, "`Gyro Yaw Right`"); m_imu_gyroscope->SetControlExpression(5, "`Gyro Yaw Right`");
for (int i = 0; i < 4; ++i)
{
m_ir_passthrough->SetControlExpression(i * 3 + 0, fmt::format("`IR Object {} X`", i + 1));
m_ir_passthrough->SetControlExpression(i * 3 + 1, fmt::format("`IR Object {} Y`", i + 1));
m_ir_passthrough->SetControlExpression(i * 3 + 2, fmt::format("`IR Object {} Size`", i + 1));
}
#endif #endif
// Enable Nunchuk: // Enable Nunchuk:

View File

@ -34,6 +34,7 @@ class Force;
class IMUAccelerometer; class IMUAccelerometer;
class IMUGyroscope; class IMUGyroscope;
class IMUCursor; class IMUCursor;
class IRPassthrough;
class ModifySettingsButton; class ModifySettingsButton;
class Output; class Output;
class Tilt; class Tilt;
@ -59,6 +60,7 @@ enum class WiimoteGroup
IMUAccelerometer, IMUAccelerometer,
IMUGyroscope, IMUGyroscope,
IMUPoint, IMUPoint,
IRPassthrough,
}; };
enum class NunchukGroup; enum class NunchukGroup;
@ -121,6 +123,7 @@ public:
static constexpr const char* ACCELEROMETER_GROUP = "IMUAccelerometer"; static constexpr const char* ACCELEROMETER_GROUP = "IMUAccelerometer";
static constexpr const char* GYROSCOPE_GROUP = "IMUGyroscope"; static constexpr const char* GYROSCOPE_GROUP = "IMUGyroscope";
static constexpr const char* IR_GROUP = "IR"; static constexpr const char* IR_GROUP = "IR";
static constexpr const char* IR_PASSTHROUGH_GROUP = "IRPassthrough";
static constexpr const char* A_BUTTON = "A"; static constexpr const char* A_BUTTON = "A";
static constexpr const char* B_BUTTON = "B"; static constexpr const char* B_BUTTON = "B";
@ -300,6 +303,7 @@ private:
ControllerEmu::IMUAccelerometer* m_imu_accelerometer; ControllerEmu::IMUAccelerometer* m_imu_accelerometer;
ControllerEmu::IMUGyroscope* m_imu_gyroscope; ControllerEmu::IMUGyroscope* m_imu_gyroscope;
ControllerEmu::IMUCursor* m_imu_ir; ControllerEmu::IMUCursor* m_imu_ir;
ControllerEmu::IRPassthrough* m_ir_passthrough;
ControllerEmu::SettingValue<bool> m_sideways_setting; ControllerEmu::SettingValue<bool> m_sideways_setting;
ControllerEmu::SettingValue<bool> m_upright_setting; ControllerEmu::SettingValue<bool> m_upright_setting;

View File

@ -504,6 +504,7 @@
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IMUAccelerometer.h" /> <ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IMUAccelerometer.h" />
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IMUCursor.h" /> <ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IMUCursor.h" />
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IMUGyroscope.h" /> <ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IMUGyroscope.h" />
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\IRPassthrough.h" />
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\MixedTriggers.h" /> <ClInclude Include="InputCommon\ControllerEmu\ControlGroup\MixedTriggers.h" />
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\ModifySettingsButton.h" /> <ClInclude Include="InputCommon\ControllerEmu\ControlGroup\ModifySettingsButton.h" />
<ClInclude Include="InputCommon\ControllerEmu\ControlGroup\Slider.h" /> <ClInclude Include="InputCommon\ControllerEmu\ControlGroup\Slider.h" />
@ -1162,6 +1163,7 @@
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IMUAccelerometer.cpp" /> <ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IMUAccelerometer.cpp" />
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IMUCursor.cpp" /> <ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IMUCursor.cpp" />
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IMUGyroscope.cpp" /> <ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IMUGyroscope.cpp" />
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\IRPassthrough.cpp" />
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\MixedTriggers.cpp" /> <ClCompile Include="InputCommon\ControllerEmu\ControlGroup\MixedTriggers.cpp" />
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\ModifySettingsButton.cpp" /> <ClCompile Include="InputCommon\ControllerEmu\ControlGroup\ModifySettingsButton.cpp" />
<ClCompile Include="InputCommon\ControllerEmu\ControlGroup\Slider.cpp" /> <ClCompile Include="InputCommon\ControllerEmu\ControlGroup\Slider.cpp" />

View File

@ -48,6 +48,8 @@ void WiimoteEmuMotionControlIMU::CreateMainLayout()
auto* groups_layout = new QHBoxLayout(); auto* groups_layout = new QHBoxLayout();
groups_layout->addWidget( groups_layout->addWidget(
CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUPoint))); CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUPoint)));
groups_layout->addWidget(
CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IRPassthrough)));
groups_layout->addWidget(CreateGroupBox( groups_layout->addWidget(CreateGroupBox(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUAccelerometer))); Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUAccelerometer)));
groups_layout->addWidget( groups_layout->addWidget(

View File

@ -37,6 +37,8 @@ add_library(inputcommon
ControllerEmu/ControlGroup/IMUCursor.h ControllerEmu/ControlGroup/IMUCursor.h
ControllerEmu/ControlGroup/IMUGyroscope.cpp ControllerEmu/ControlGroup/IMUGyroscope.cpp
ControllerEmu/ControlGroup/IMUGyroscope.h ControllerEmu/ControlGroup/IMUGyroscope.h
ControllerEmu/ControlGroup/IRPassthrough.cpp
ControllerEmu/ControlGroup/IRPassthrough.h
ControllerEmu/ControlGroup/MixedTriggers.cpp ControllerEmu/ControlGroup/MixedTriggers.cpp
ControllerEmu/ControlGroup/MixedTriggers.h ControllerEmu/ControlGroup/MixedTriggers.h
ControllerEmu/ControlGroup/ModifySettingsButton.cpp ControllerEmu/ControlGroup/ModifySettingsButton.cpp

View File

@ -49,7 +49,8 @@ enum class GroupType
Shake, Shake,
IMUAccelerometer, IMUAccelerometer,
IMUGyroscope, IMUGyroscope,
IMUCursor IMUCursor,
IRPassthrough,
}; };
class ControlGroup class ControlGroup

View File

@ -0,0 +1,51 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "InputCommon/ControllerEmu/ControlGroup/IRPassthrough.h"
#include <memory>
#include <string>
#include "Common/Common.h"
#include "Common/MathUtil.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
namespace ControllerEmu
{
IRPassthrough::IRPassthrough(std::string name_, std::string ui_name_)
: ControlGroup(std::move(name_), std::move(ui_name_), GroupType::IRPassthrough,
ControlGroup::DefaultValue::Disabled)
{
AddInput(Translatability::Translate, _trans("Object 1 X"));
AddInput(Translatability::Translate, _trans("Object 1 Y"));
AddInput(Translatability::Translate, _trans("Object 1 Size"));
AddInput(Translatability::Translate, _trans("Object 2 X"));
AddInput(Translatability::Translate, _trans("Object 2 Y"));
AddInput(Translatability::Translate, _trans("Object 2 Size"));
AddInput(Translatability::Translate, _trans("Object 3 X"));
AddInput(Translatability::Translate, _trans("Object 3 Y"));
AddInput(Translatability::Translate, _trans("Object 3 Size"));
AddInput(Translatability::Translate, _trans("Object 4 X"));
AddInput(Translatability::Translate, _trans("Object 4 Y"));
AddInput(Translatability::Translate, _trans("Object 4 Size"));
}
ControlState IRPassthrough::GetObjectPositionX(size_t object_index) const
{
return controls[object_index * 3 + 0]->GetState();
}
ControlState IRPassthrough::GetObjectPositionY(size_t object_index) const
{
return controls[object_index * 3 + 1]->GetState();
}
ControlState IRPassthrough::GetObjectSize(size_t object_index) const
{
return controls[object_index * 3 + 2]->GetState();
}
} // namespace ControllerEmu

View File

@ -0,0 +1,23 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ControllerEmu
{
class IRPassthrough : public ControlGroup
{
public:
IRPassthrough(std::string name, std::string ui_name);
ControlState GetObjectPositionX(size_t object_index) const;
ControlState GetObjectPositionY(size_t object_index) const;
ControlState GetObjectSize(size_t object_index) const;
};
} // namespace ControllerEmu

View File

@ -201,6 +201,17 @@ Device::Device(std::unique_ptr<WiimoteReal::Wiimote> wiimote) : m_wiimote(std::m
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.distance, "IR Distance", 1)); AddInput(new UndetectableAnalogInput<float>(&m_ir_state.distance, "IR Distance", 1));
// Raw IR Objects.
for (std::size_t i = 0; i < 4; ++i)
{
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.raw_ir_object_position[i].x,
fmt::format("IR Object {} X", i + 1), 1));
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.raw_ir_object_position[i].y,
fmt::format("IR Object {} Y", i + 1), 1));
AddInput(new UndetectableAnalogInput<float>(&m_ir_state.raw_ir_object_size[i],
fmt::format("IR Object {} Size", i + 1), 1));
}
// Raw gyroscope. // Raw gyroscope.
static constexpr std::array<std::array<const char*, 2>, 3> gyro_names = {{ static constexpr std::array<std::array<const char*, 2>, 3> gyro_names = {{
{"Gyro Pitch Down", "Gyro Pitch Up"}, {"Gyro Pitch Down", "Gyro Pitch Up"},
@ -1178,8 +1189,7 @@ void Device::ProcessInputReport(WiimoteReal::Report& report)
// Process IR data. // Process IR data.
if (manipulator->HasIR() && m_ir_state.IsFullyConfigured()) if (manipulator->HasIR() && m_ir_state.IsFullyConfigured())
{ {
m_ir_state.ProcessData( m_ir_state.ProcessData(*manipulator);
Common::BitCastPtr<std::array<WiimoteEmu::IRBasic, 2>>(manipulator->GetIRDataPtr()));
} }
// Process extension data. // Process extension data.
@ -1251,7 +1261,7 @@ void Device::UpdateOrientation()
float(MathUtil::PI); float(MathUtil::PI);
} }
void Device::IRState::ProcessData(const std::array<WiimoteEmu::IRBasic, 2>& data) void Device::IRState::ProcessData(const DataReportManipulator& manipulator)
{ {
// A better implementation might extrapolate points when they fall out of camera view. // A better implementation might extrapolate points when they fall out of camera view.
// But just averaging visible points actually seems to work very well. // But just averaging visible points actually seems to work very well.
@ -1263,18 +1273,54 @@ void Device::IRState::ProcessData(const std::array<WiimoteEmu::IRBasic, 2>& data
const auto camera_max = IRObject(WiimoteEmu::CameraLogic::CAMERA_RES_X - 1, const auto camera_max = IRObject(WiimoteEmu::CameraLogic::CAMERA_RES_X - 1,
WiimoteEmu::CameraLogic::CAMERA_RES_Y - 1); WiimoteEmu::CameraLogic::CAMERA_RES_Y - 1);
const auto add_point = [&](IRObject point) { const auto add_point = [&](IRObject point, u8 size, size_t idx) {
// Non-visible points are 0xFF-filled. // Non-visible points are 0xFF-filled.
if (point.y > camera_max.y) if (point.y > camera_max.y)
{
raw_ir_object_position[idx].x = 0.0f;
raw_ir_object_position[idx].y = 0.0f;
raw_ir_object_size[idx] = 0.0f;
return; return;
}
raw_ir_object_position[idx].x = static_cast<float>(point.x) / camera_max.x;
raw_ir_object_position[idx].y = static_cast<float>(point.y) / camera_max.y;
raw_ir_object_size[idx] = static_cast<float>(size) / 15.0f;
points.Push(Common::Vec2(point)); points.Push(Common::Vec2(point));
}; };
for (auto& block : data) size_t object_index = 0;
switch (manipulator.GetIRReportFormat())
{ {
add_point(block.GetObject1()); case IRReportFormat::Basic:
add_point(block.GetObject2()); {
const std::array<WiimoteEmu::IRBasic, 2> data =
Common::BitCastPtr<std::array<WiimoteEmu::IRBasic, 2>>(manipulator.GetIRDataPtr());
for (const auto& block : data)
{
// size is not reported by IRBasic, just assume a typical size
add_point(block.GetObject1(), 2, object_index);
++object_index;
add_point(block.GetObject2(), 2, object_index);
++object_index;
}
break;
}
case IRReportFormat::Extended:
{
const std::array<WiimoteEmu::IRExtended, 4> data =
Common::BitCastPtr<std::array<WiimoteEmu::IRExtended, 4>>(manipulator.GetIRDataPtr());
for (const auto& object : data)
{
add_point(object.GetPosition(), object.size, object_index);
++object_index;
}
break;
}
default:
// unsupported format
return;
} }
is_hidden = !points.Count(); is_hidden = !points.Count();

View File

@ -126,7 +126,7 @@ private:
{ {
static u32 GetDesiredIRSensitivity(); static u32 GetDesiredIRSensitivity();
void ProcessData(const std::array<WiimoteEmu::IRBasic, 2>&); void ProcessData(const DataReportManipulator& manipulator);
bool IsFullyConfigured() const; bool IsFullyConfigured() const;
u32 current_sensitivity = u32(-1); u32 current_sensitivity = u32(-1);
@ -139,6 +139,9 @@ private:
float distance = 0; float distance = 0;
bool is_hidden = true; bool is_hidden = true;
std::array<Common::Vec2, 4> raw_ir_object_position;
std::array<float, 4> raw_ir_object_size;
}; };
class ReportHandler class ReportHandler