diff --git a/.gitmodules b/.gitmodules
index e79738aba1..50cdb67ab6 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -100,3 +100,7 @@
path = 3rdparty/opencv/opencv
url = ../../Megamouse/opencv_minimal.git
ignore = dirty
+[submodule "3rdparty/fusion/fusion"]
+ path = 3rdparty/fusion/fusion
+ url = ../../xioTechnologies/Fusion.git
+ ignore = dirty
diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt
index 57a45d7be5..434b870b52 100644
--- a/3rdparty/CMakeLists.txt
+++ b/3rdparty/CMakeLists.txt
@@ -346,6 +346,9 @@ add_subdirectory(rtmidi EXCLUDE_FROM_ALL)
# OPENCV
add_subdirectory(opencv EXCLUDE_FROM_ALL)
+# FUSION
+add_subdirectory(fusion EXCLUDE_FROM_ALL)
+
# add nice ALIAS targets for ease of use
if(USE_SYSTEM_LIBUSB)
add_library(3rdparty::libusb ALIAS usb-1.0-shared)
@@ -377,3 +380,4 @@ add_library(3rdparty::sdl2 ALIAS ${SDL2_TARGET})
add_library(3rdparty::miniupnpc ALIAS libminiupnpc-static)
add_library(3rdparty::rtmidi ALIAS rtmidi)
add_library(3rdparty::opencv ALIAS ${OPENCV_TARGET})
+add_library(3rdparty::fusion ALIAS Fusion)
diff --git a/3rdparty/fusion/CMakeLists.txt b/3rdparty/fusion/CMakeLists.txt
new file mode 100644
index 0000000000..099056a8de
--- /dev/null
+++ b/3rdparty/fusion/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(fusion EXCLUDE_FROM_ALL)
diff --git a/3rdparty/fusion/fusion b/3rdparty/fusion/fusion
new file mode 160000
index 0000000000..fecf2f0af3
--- /dev/null
+++ b/3rdparty/fusion/fusion
@@ -0,0 +1 @@
+Subproject commit fecf2f0af3bd23cbba553ceedc2bc6c1cd410fc1
diff --git a/3rdparty/fusion/fusion.vcxproj b/3rdparty/fusion/fusion.vcxproj
new file mode 100644
index 0000000000..71e669be00
--- /dev/null
+++ b/3rdparty/fusion/fusion.vcxproj
@@ -0,0 +1,75 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fusion
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}
+
+
+
+
+
+ StaticLibrary
+ Unicode
+
+
+
+ x64
+
+
+ $(SolutionDir)build\lib\$(Configuration)-$(Platform)\
+ $(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\
+
+
+ $(SolutionDir)build\lib\$(Configuration)-$(Platform)\
+ $(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sync
+ MaxSpeed
+
+
+
+
+
+
\ No newline at end of file
diff --git a/3rdparty/fusion/fusion.vcxproj.filters b/3rdparty/fusion/fusion.vcxproj.filters
new file mode 100644
index 0000000000..85764a4f97
--- /dev/null
+++ b/3rdparty/fusion/fusion.vcxproj.filters
@@ -0,0 +1,29 @@
+
+
+
+
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/rpcs3.sln b/rpcs3.sln
index 9ca661550b..26bc66179b 100644
--- a/rpcs3.sln
+++ b/rpcs3.sln
@@ -7,6 +7,7 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emucore", "rpcs3\emucore.vcxproj", "{C4A10229-4712-4BD2-B63E-50D93C67A038}"
ProjectSection(ProjectDependencies) = postProject
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3} = {2C902C67-985C-4BE0-94A3-E0FE2EB929A3}
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF} = {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}
{8846A9AA-5539-4C91-8301-F54260E1A07A} = {8846A9AA-5539-4C91-8301-F54260E1A07A}
{939FE206-1182-ABC3-1234-FEAB88E98404} = {939FE206-1182-ABC3-1234-FEAB88E98404}
@@ -46,6 +47,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxpro
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3} = {2C902C67-985C-4BE0-94A3-E0FE2EB929A3}
{3384223A-6D97-4799-9862-359F85312892} = {3384223A-6D97-4799-9862-359F85312892}
{349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {349EE8F9-7D25-4909-AAF5-FF3FADE72187}
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF} = {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D} = {3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}
{508C291A-3D18-49F5-B25D-F7C8DB92CB21} = {508C291A-3D18-49F5-B25D-F7C8DB92CB21}
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C} = {5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}
@@ -103,6 +105,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7zip", "3rdparty\7zip\7zip.
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openal-soft", "3rdparty\openal\openal-soft.vcxproj", "{8846A9AA-5539-4C91-8301-F54260E1A07A}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fusion", "3rdparty\fusion\fusion.vcxproj", "{3C67A2FF-4710-402A-BE3E-31B0CB0576DF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -211,6 +215,10 @@ Global
{8846A9AA-5539-4C91-8301-F54260E1A07A}.Debug|x64.Build.0 = Debug|x64
{8846A9AA-5539-4C91-8301-F54260E1A07A}.Release|x64.ActiveCfg = Release|x64
{8846A9AA-5539-4C91-8301-F54260E1A07A}.Release|x64.Build.0 = Release|x64
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}.Debug|x64.ActiveCfg = Debug|x64
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}.Debug|x64.Build.0 = Debug|x64
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}.Release|x64.ActiveCfg = Release|x64
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -243,6 +251,7 @@ Global
{4E52A41A-F33B-4C7A-8C36-A1A6B4F4277C} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
{8846A9AA-5539-4C91-8301-F54260E1A07A} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
+ {3C67A2FF-4710-402A-BE3E-31B0CB0576DF} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt
index 04a928fa72..059d0a4dbf 100644
--- a/rpcs3/CMakeLists.txt
+++ b/rpcs3/CMakeLists.txt
@@ -82,6 +82,7 @@ target_sources(rpcs3
Input/mm_joystick_handler.cpp
Input/pad_thread.cpp
Input/product_info.cpp
+ Input/ps_move_calibration.cpp
Input/ps_move_config.cpp
Input/ps_move_handler.cpp
Input/ps_move_tracker.cpp
@@ -110,6 +111,7 @@ target_link_libraries(rpcs3
3rdparty::libcurl
3rdparty::zlib
3rdparty::opencv
+ 3rdparty::fusion
${ADDITIONAL_LIBS})
# Unix display manager
diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp
index d94c4dbd70..7e1e95ae61 100644
--- a/rpcs3/Emu/Cell/Modules/cellGem.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp
@@ -903,7 +903,7 @@ static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_con
}
}
-static inline void pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
+static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max, const ps_move_data& move_data)
{
const auto& shared_data = g_fxo->get();
@@ -932,16 +932,28 @@ static inline void pos_to_gem_state(u32 gem_num, const gem_config::gem_controlle
gem_state->pos[2] = controller.distance_mm;
gem_state->pos[3] = 0.f;
- gem_state->quat[0] = 320.f - image_x;
- gem_state->quat[1] = (y_pos / scaling_width) - 180.f;
- gem_state->quat[2] = 1200.f;
-
// TODO: calculate handle position based on our world coordinate and the angles
gem_state->handle_pos[0] = camera_x;
gem_state->handle_pos[1] = camera_y;
gem_state->handle_pos[2] = controller.distance_mm + 10.0f;
gem_state->handle_pos[3] = 0.f;
+ // Calculate orientation
+ if (g_cfg.io.move == move_handler::real)
+ {
+ gem_state->quat[0] = move_data.quaternion[1]; // x
+ gem_state->quat[1] = move_data.quaternion[2]; // y
+ gem_state->quat[2] = move_data.quaternion[3]; // z
+ gem_state->quat[3] = move_data.quaternion[0]; // w
+ }
+ else
+ {
+ gem_state->quat[0] = 320.f - image_x;
+ gem_state->quat[1] = (y_pos / scaling_width) - 180.f;
+ gem_state->quat[2] = 1200.f;
+ gem_state->quat[3] = 1.f;
+ }
+
if (g_cfg.io.show_move_cursor)
{
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
@@ -1056,7 +1068,7 @@ static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr&
}
template
-static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, T& gem_state)
+static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, T& gem_state)
{
if (!gem_state || !is_input_allowed())
{
@@ -1078,7 +1090,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller&
if constexpr (std::is_same_v>)
{
- pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
+ pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y, {});
}
else if constexpr (std::is_same_v>)
{
@@ -1087,7 +1099,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller&
}
template
-static void ps_move_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, T& gem_state)
+static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, T& gem_state)
{
if (!gem_state || !is_input_allowed())
{
@@ -1114,7 +1126,7 @@ static void ps_move_pos_to_gem_state(u32 gem_num, const gem_config::gem_controll
gem_state->accel[1] = pad->move_data.accelerometer_y * 1000; // linear velocity in mm/s²
gem_state->accel[2] = pad->move_data.accelerometer_z * 1000; // linear velocity in mm/s²
- pos_to_gem_state(gem_num, controller, gem_state, info.x_pos, info.y_pos, info.x_max, info.y_max);
+ pos_to_gem_state(gem_num, controller, gem_state, info.x_pos, info.y_pos, info.x_max, info.y_max, pad->move_data);
}
else if constexpr (std::is_same_v>)
{
@@ -1272,7 +1284,7 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t& digital_buttons, be_t
-static void mouse_pos_to_gem_state(u32 mouse_no, const gem_config::gem_controller& controller, T& gem_state)
+static void mouse_pos_to_gem_state(u32 mouse_no, gem_config::gem_controller& controller, T& gem_state)
{
if (!gem_state || !is_input_allowed())
{
@@ -1295,7 +1307,7 @@ static void mouse_pos_to_gem_state(u32 mouse_no, const gem_config::gem_controlle
if constexpr (std::is_same_v>)
{
- pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
+ pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max, {});
}
else if constexpr (std::is_same_v>)
{
@@ -1345,7 +1357,7 @@ static bool gun_input_to_pad(u32 gem_no, be_t& digital_buttons, be_t&
}
template
-static void gun_pos_to_gem_state(u32 gem_no, const gem_config::gem_controller& controller, T& gem_state)
+static void gun_pos_to_gem_state(u32 gem_no, gem_config::gem_controller& controller, T& gem_state)
{
if (!gem_state || !is_input_allowed())
return;
@@ -1363,7 +1375,7 @@ static void gun_pos_to_gem_state(u32 gem_no, const gem_config::gem_controller& c
if constexpr (std::is_same_v>)
{
- pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max);
+ pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max, {});
}
else if constexpr (std::is_same_v>)
{
@@ -1800,7 +1812,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag
if (g_cfg.io.move != move_handler::null)
{
auto& shared_data = g_fxo->get();
- const auto& controller = gem.controllers[gem_num];
+ auto& controller = gem.controllers[gem_num];
gem_image_state->frame_timestamp = shared_data.frame_timestamp_us.load();
gem_image_state->timestamp = gem_image_state->frame_timestamp + 10;
@@ -2130,7 +2142,6 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptrtimestamp = (get_guest_system_time() - gem.start_timestamp_us);
gem_state->camera_pitch_angle = 0.f;
- gem_state->quat[3] = 1.f;
switch (g_cfg.io.move)
{
diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h
index 631e9d6c4e..77d56fdf59 100644
--- a/rpcs3/Emu/Io/pad_types.h
+++ b/rpcs3/Emu/Io/pad_types.h
@@ -466,12 +466,13 @@ struct ps_move_data
bool external_device_read_requested = false;
bool external_device_write_requested = false;
+ std::array quaternion { 1.0f, 0.0f, 0.0f, 0.0f }; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
f32 accelerometer_x = 0; // linear velocity in m/s²
f32 accelerometer_y = 0; // linear velocity in m/s²
f32 accelerometer_z = 0; // linear velocity in m/s²
- f32 gyro_x = 0; // linear velocity in m/s²
- f32 gyro_y = 0; // linear velocity in m/s²
- f32 gyro_z = 0; // linear velocity in m/s²
+ f32 gyro_x = 0; // angular velocity in rad/s
+ f32 gyro_y = 0; // angular velocity in rad/s
+ f32 gyro_z = 0; // angular velocity in rad/s
s16 temperature = 0;
};
diff --git a/rpcs3/Input/ps_move_calibration.cpp b/rpcs3/Input/ps_move_calibration.cpp
new file mode 100644
index 0000000000..aaadc2b89a
--- /dev/null
+++ b/rpcs3/Input/ps_move_calibration.cpp
@@ -0,0 +1,240 @@
+#include "stdafx.h"
+#include "ps_move_calibration.h"
+
+LOG_CHANNEL(move_log, "Move");
+
+// This is basically the same as in ps move api
+
+static inline s32 psmove_calibration_decode_16bit_unsigned(const u8* data, u32 offset)
+{
+ const u8 low = data[offset] & 0xFF;
+ const u8 high = (data[offset + 1]) & 0xFF;
+ return (low | (high << 8)) - zero_shift;
+}
+
+static inline s16 psmove_calibration_decode_16bit_signed(const u8* data, u32 offset)
+{
+ const u16 low = data[offset] & 0xFF;
+ const u16 high = (data[offset + 1]) & 0xFF;
+ return static_cast(low | (high << 8));
+}
+
+static inline u32 psmove_calibration_decode_12bits(const u8* data, u32 offset)
+{
+ const u8 low = data[offset] & 0xFF;
+ const u8 high = (data[offset + 1]) & 0xFF;
+ return low | (high << 8);
+}
+
+static inline f32 psmove_calibration_decode_float(const u8* data, u32 offset)
+{
+ union
+ {
+ uint32_t u32;
+ float f;
+ } v {
+ .u32 = static_cast( (data[offset] & 0xFF) |
+ ((data[offset + 1] & 0xFF) << 8) |
+ ((data[offset + 2] & 0xFF) << 16) |
+ ((data[offset + 3] & 0xFF) << 24))
+ };
+
+ return v.f;
+}
+
+static void psmove_dump_calibration(const reports::ps_move_calibration_blob& calibration, const ps_move_device& device)
+{
+ int t, x, y, z;
+ float fx, fy, fz;
+
+ const u8* data = calibration.data.data();
+
+ switch (device.model)
+ {
+ case ps_move_model::ZCM1:
+ t = psmove_calibration_decode_12bits(data, 0x02);
+ move_log.error("Temperature: 0x%04X", t);
+ for (int orientation = 0; orientation < 6; orientation++)
+ {
+ x = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * orientation);
+ y = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * orientation + 2);
+ z = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * orientation + 4);
+ move_log.error("Orientation #%d: (%5d | %5d | %5d)", orientation, x, y, z);
+ }
+
+ t = psmove_calibration_decode_12bits(data, 0x42);
+ move_log.error("Temperature: 0x%04X", t);
+ for (int orientation = 0; orientation < 3; orientation++)
+ {
+ x = psmove_calibration_decode_16bit_unsigned(data, 0x46 + 8 * orientation);
+ y = psmove_calibration_decode_16bit_unsigned(data, 0x46 + 8 * orientation + 2);
+ z = psmove_calibration_decode_16bit_unsigned(data, 0x46 + 8 * orientation + 4);
+ move_log.error("Gyro %c, 80 rpm: (%5d | %5d | %5d)", "XYZ"[orientation], x, y, z);
+ }
+
+ t = psmove_calibration_decode_12bits(data, 0x28);
+ x = psmove_calibration_decode_16bit_unsigned(data, 0x2a);
+ y = psmove_calibration_decode_16bit_unsigned(data, 0x2a + 2);
+ z = psmove_calibration_decode_16bit_unsigned(data, 0x2a + 4);
+ move_log.error("Temperature: 0x%04X", t);
+ move_log.error("Gyro, 0 rpm (@0x2a): (%5d | %5d | %5d)", x, y, z);
+
+ t = psmove_calibration_decode_12bits(data, 0x30);
+ x = psmove_calibration_decode_16bit_unsigned(data, 0x32);
+ y = psmove_calibration_decode_16bit_unsigned(data, 0x32 + 2);
+ z = psmove_calibration_decode_16bit_unsigned(data, 0x32 + 4);
+ move_log.error("Temperature: 0x%04X", t);
+ move_log.error("Gyro, 0 rpm (@0x32): (%5d | %5d | %5d)", x, y, z);
+
+ t = psmove_calibration_decode_12bits(data, 0x5c);
+ fx = psmove_calibration_decode_float(data, 0x5e);
+ fy = psmove_calibration_decode_float(data, 0x5e + 4);
+ fz = psmove_calibration_decode_float(data, 0x5e + 8);
+ move_log.error("Temperature: 0x%04X", t);
+ move_log.error("Vector @0x5e: (%f | %f | %f)", fx, fy, fz);
+
+ fx = psmove_calibration_decode_float(data, 0x6a);
+ fy = psmove_calibration_decode_float(data, 0x6a + 4);
+ fz = psmove_calibration_decode_float(data, 0x6a + 8);
+ move_log.error("Vector @0x6a: (%f | %f | %f)", fx, fy, fz);
+
+ move_log.error("byte @0x3f: 0x%02x", static_cast(data[0x3f]));
+ move_log.error("float @0x76: %f", psmove_calibration_decode_float(data, 0x76));
+ move_log.error("float @0x7a: %f", psmove_calibration_decode_float(data, 0x7a));
+ break;
+ case ps_move_model::ZCM2:
+ for (int orientation = 0; orientation < 6; orientation++)
+ {
+ x = psmove_calibration_decode_16bit_signed(data, 0x04 + 6 * orientation);
+ y = psmove_calibration_decode_16bit_signed(data, 0x04 + 6 * orientation + 2);
+ z = psmove_calibration_decode_16bit_signed(data, 0x04 + 6 * orientation + 4);
+ move_log.error("Orientation #%d: (%5d | %5d | %5d)", orientation, x, y, z);
+ }
+
+ x = psmove_calibration_decode_16bit_signed(data, 0x26);
+ y = psmove_calibration_decode_16bit_signed(data, 0x26 + 2);
+ z = psmove_calibration_decode_16bit_signed(data, 0x26 + 4);
+ move_log.error("Gyro Bias?, 0 rpm (@0x26): (%5d | %5d | %5d)", x, y, z);
+
+ for (int orientation = 0; orientation < 6; orientation++)
+ {
+ x = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * orientation);
+ y = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * orientation + 2);
+ z = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * orientation + 4);
+ move_log.error("Gyro %c, 90 rpm: (%5d | %5d | %5d)", "XYZXYZ"[orientation], x, y, z);
+ }
+ break;
+ }
+}
+
+void psmove_calibration_get_usb_accel_values(const reports::ps_move_calibration_blob& calibration, ps_move_device& device)
+{
+ const u8* data = calibration.data.data();
+
+ int x1 = 0, x2 = 0, y1 = 0, y2 = 0, z1 = 0, z2 = 0;
+
+ switch (device.model)
+ {
+ case ps_move_model::ZCM1:
+ // Minimum (negative) value of each axis
+ x1 = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * 1);
+ y1 = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * 5 + 2);
+ z1 = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * 2 + 4);
+
+ // Maximum (positive) value of each axis
+ x2 = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * 3);
+ y2 = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * 4 + 2);
+ z2 = psmove_calibration_decode_16bit_unsigned(data, 0x04 + 6 * 0 + 4);
+ break;
+ case ps_move_model::ZCM2:
+ // Minimum (negative) value of each axis
+ x1 = psmove_calibration_decode_16bit_signed(data, 0x02 + 6 * 1);
+ y1 = psmove_calibration_decode_16bit_signed(data, 0x02 + 6 * 3 + 2);
+ z1 = psmove_calibration_decode_16bit_signed(data, 0x02 + 6 * 5 + 4);
+
+ // Maximum (positive) value of each axis
+ x2 = psmove_calibration_decode_16bit_signed(data, 0x02 + 6 * 0);
+ y2 = psmove_calibration_decode_16bit_signed(data, 0x02 + 6 * 2 + 2);
+ z2 = psmove_calibration_decode_16bit_signed(data, 0x02 + 6 * 4 + 4);
+ break;
+ }
+
+ device.calibration.accel_x_factor = 2.0f / static_cast(x2 - x1);
+ device.calibration.accel_y_factor = 2.0f / static_cast(y2 - y1);
+ device.calibration.accel_z_factor = 2.0f / static_cast(z2 - z1);
+
+ device.calibration.accel_x_offset = -(device.calibration.accel_x_factor * static_cast(x1)) - 1.0f;
+ device.calibration.accel_y_offset = -(device.calibration.accel_y_factor * static_cast(x1)) - 1.0f;
+ device.calibration.accel_z_offset = -(device.calibration.accel_z_factor * static_cast(x1)) - 1.0f;
+}
+
+void psmove_calibration_get_usb_gyro_values(const reports::ps_move_calibration_blob& calibration, ps_move_device& device)
+{
+ const u8* data = calibration.data.data();
+
+ constexpr f32 PI = 3.14159265f;
+ constexpr f32 rpm_to_rad_per_sec = (2.0f * PI) / 60.0f;
+
+ switch (device.model)
+ {
+ case ps_move_model::ZCM1:
+ {
+ const int bx = psmove_calibration_decode_16bit_unsigned(data, 0x2a);
+ const int by = psmove_calibration_decode_16bit_unsigned(data, 0x2a + 2);
+ const int bz = psmove_calibration_decode_16bit_unsigned(data, 0x2a + 4);
+
+ const int x = psmove_calibration_decode_16bit_unsigned(data, 0x46 + 8 * 0) - bx;
+ const int y = psmove_calibration_decode_16bit_unsigned(data, 0x46 + 8 * 1 + 2) - by;
+ const int z = psmove_calibration_decode_16bit_unsigned(data, 0x46 + 8 * 2 + 4) - bz;
+
+ constexpr f32 calibration_rpm = 80.0f;
+ constexpr f32 factor = calibration_rpm * rpm_to_rad_per_sec;
+
+ // Per frame drift taken into account using adjusted gain values
+ device.calibration.gyro_x_gain = factor / static_cast(x);
+ device.calibration.gyro_y_gain = factor / static_cast(y);
+ device.calibration.gyro_z_gain = factor / static_cast(z);
+ device.calibration.gyro_x_offset = 0;
+ device.calibration.gyro_y_offset = 0;
+ device.calibration.gyro_z_offset = 0;
+ break;
+ }
+ case ps_move_model::ZCM2:
+ {
+ // Minimum (negative) value of each axis
+ const int x1 = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * 3);
+ const int y1 = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * 4 + 2);
+ const int z1 = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * 5 + 4);
+
+ // Maximum (positive) values of each axis
+ const int x2 = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * 0);
+ const int y2 = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * 1 + 2);
+ const int z2 = psmove_calibration_decode_16bit_signed(data, 0x30 + 6 * 2 + 4);
+
+ const int dx = psmove_calibration_decode_16bit_signed(data, 0x26);
+ const int dy = psmove_calibration_decode_16bit_signed(data, 0x26 + 2);
+ const int dz = psmove_calibration_decode_16bit_signed(data, 0x26 + 4);
+
+ constexpr f32 calibration_rpm = 90.0f;
+ constexpr f32 calibration_hi = calibration_rpm * rpm_to_rad_per_sec;
+ constexpr f32 calibration_low = -calibration_rpm * rpm_to_rad_per_sec;
+ constexpr f32 factor = calibration_hi - calibration_low;
+
+ // Compute the gain value (the slope of the gyro reading/angular speed line)
+ device.calibration.gyro_x_gain = factor / static_cast(x2 - x1);
+ device.calibration.gyro_y_gain = factor / static_cast(y2 - y1);
+ device.calibration.gyro_z_gain = factor / static_cast(z2 - z1);
+ device.calibration.gyro_x_offset = static_cast(dx);
+ device.calibration.gyro_y_offset = static_cast(dy);
+ device.calibration.gyro_z_offset = static_cast(dz);
+ break;
+ }
+ }
+}
+
+void psmove_parse_calibration(const reports::ps_move_calibration_blob& calibration, ps_move_device& device)
+{
+ psmove_dump_calibration(calibration, device);
+ psmove_calibration_get_usb_accel_values(calibration, device);
+ psmove_calibration_get_usb_gyro_values(calibration, device);
+}
diff --git a/rpcs3/Input/ps_move_calibration.h b/rpcs3/Input/ps_move_calibration.h
new file mode 100644
index 0000000000..a1e362fa9f
--- /dev/null
+++ b/rpcs3/Input/ps_move_calibration.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "ps_move_handler.h"
+
+void psmove_parse_calibration(const reports::ps_move_calibration_blob& calibration, ps_move_device& device);
diff --git a/rpcs3/Input/ps_move_handler.cpp b/rpcs3/Input/ps_move_handler.cpp
index cee24d208f..38dd3d6551 100644
--- a/rpcs3/Input/ps_move_handler.cpp
+++ b/rpcs3/Input/ps_move_handler.cpp
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "ps_move_handler.h"
+#include "ps_move_calibration.h"
#include "Emu/Io/pad_config.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
@@ -57,11 +58,6 @@ namespace
usb_charging = 0xEE,
usb_charged = 0xEF,
};
-
- enum
- {
- zero_shift = 0x8000,
- };
}
const ps_move_input_report_common& ps_move_device::input_report_common() const
@@ -223,7 +219,7 @@ hid_device* ps_move_handler::connect_move_device(ps_move_device* device, std::st
if (hid_set_nonblocking(device->bt_device, 1) == -1)
{
- move_log.error("check_add_device: hid_set_nonblocking failed! Reason: %s", hid_error(device->bt_device));
+ move_log.error("connect_move_device: hid_set_nonblocking failed! Reason: %s", hid_error(device->bt_device));
device->close();
return nullptr;
}
@@ -239,7 +235,7 @@ hid_device* ps_move_handler::connect_move_device(ps_move_device* device, std::st
if (hid_set_nonblocking(device->hidDevice, 1) == -1)
{
- move_log.error("check_add_device: hid_set_nonblocking failed! Reason: %s", hid_error(device->hidDevice));
+ move_log.error("connect_move_device: hid_set_nonblocking failed! Reason: %s", hid_error(device->hidDevice));
device->close();
return nullptr;
}
@@ -315,6 +311,60 @@ void ps_move_handler::check_add_device(hid_device* hidDevice, std::string_view p
device->hidDevice = hidDevice;
device->path = path;
+ // Get calibration
+ device->calibration.is_valid = true;
+
+ ps_move_calibration_blob calibration {};
+
+ for (int i = 0; i < 2; i++)
+ {
+ std::array cal {};
+ cal[0] = 0x10;
+ const int res = hid_get_feature_report(device->hidDevice, cal.data(), cal.size());
+ if (res < 0)
+ {
+ move_log.error("connect_move_device: hid_get_feature_report 0x10 (calibration) failed! result=%d, error=%s", res, hid_error(device->hidDevice));
+ device->calibration.is_valid = false;
+ break;
+ }
+
+ int src_offset = 0;
+ int dest_offset = 0;
+
+ if ((cal[1] == 0x01 && device->model == ps_move_model::ZCM1) ||
+ (cal[1] == 0x81 && device->model == ps_move_model::ZCM2))
+ {
+ // This is the second block
+ dest_offset = PSMOVE_CALIBRATION_SIZE;
+ src_offset = 2;
+ }
+ else if (cal[1] == 0x82 && device->model == ps_move_model::ZCM1)
+ {
+ // This is the third block
+ dest_offset = 2 * PSMOVE_CALIBRATION_SIZE - 2;
+ src_offset = 2;
+ }
+ else if (cal[1] != 0x00) // Check if this is the first block (offsets stay 0)
+ {
+ move_log.error("connect_move_device: Failed to read calibration: cal=0x%x'", cal[1]);
+ device->calibration.is_valid = false;
+ break;
+ }
+
+ std::memcpy(&calibration.data[dest_offset], &cal[src_offset], cal.size() - src_offset);
+ }
+
+ if (device->calibration.is_valid)
+ {
+ psmove_parse_calibration(calibration, *device);
+ }
+
+ // Initialize Fusion
+ FusionAhrsInitialise(&device->ahrs);
+ device->ahrs.settings.convention = FusionConvention::FusionConventionEnu;
+ FusionAhrsSetSettings(&device->ahrs, &device->ahrs.settings);
+ FusionAhrsReset(&device->ahrs);
+
// Activate
if (send_output_report(device) == -1)
{
@@ -620,34 +670,48 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding)
const ps_move_input_report_common& input = dev->input_report_common();
- constexpr f32 MOVE_ONE_G = 4096.0f; // This is just a rough estimate and probably depends on the device
-
// The default position is flat on the ground, pointing forward.
// The accelerometers constantly measure G forces.
// The gyros measure changes in orientation and will reset when the device isn't moved anymore.
- s16 accel_x = input.accel_x_2; // Increases if the device is rolled to the left
- s16 accel_y = input.accel_y_2; // Increases if the device is pitched upwards
- s16 accel_z = input.accel_z_2; // Increases if the device is moved upwards
- s16 gyro_x = input.gyro_x_2; // Increases if the device is pitched upwards
- s16 gyro_y = input.gyro_y_2; // Increases if the device is rolled to the right
- s16 gyro_z = input.gyro_z_2; // Increases if the device is yawed to the left
+ s16 accel_x = input.accel_x_1; // Increases if the device is rolled to the left
+ s16 accel_y = input.accel_y_1; // Increases if the device is pitched upwards
+ s16 accel_z = input.accel_z_1; // Increases if the device is moved upwards
+ s16 gyro_x = input.gyro_x_1; // Increases if the device is pitched upwards
+ s16 gyro_y = input.gyro_y_1; // Increases if the device is rolled to the right
+ s16 gyro_z = input.gyro_z_1; // Increases if the device is yawed to the left
if (dev->model == ps_move_model::ZCM1)
{
- accel_x -= zero_shift;
- accel_y -= zero_shift;
- accel_z -= zero_shift;
- gyro_x -= zero_shift;
- gyro_y -= zero_shift;
- gyro_z -= zero_shift;
+ accel_x = (input.accel_x_1 + input.accel_x_2) / 2 - zero_shift;
+ accel_y = (input.accel_y_1 + input.accel_y_2) / 2 - zero_shift;
+ accel_z = (input.accel_z_1 + input.accel_z_2) / 2 - zero_shift;
+ gyro_x = (input.gyro_x_1 + input.gyro_x_2) / 2 - zero_shift;
+ gyro_y = (input.gyro_y_1 + input.gyro_y_2) / 2 - zero_shift;
+ gyro_z = (input.gyro_z_1 + input.gyro_z_2) / 2 - zero_shift;
+ }
+
+ // Apply calibration
+ if (dev->calibration.is_valid)
+ {
+ pad->move_data.accelerometer_x = accel_x * dev->calibration.accel_x_factor + dev->calibration.accel_x_offset;
+ pad->move_data.accelerometer_y = accel_y * dev->calibration.accel_y_factor + dev->calibration.accel_y_offset;
+ pad->move_data.accelerometer_z = accel_z * dev->calibration.accel_z_factor + dev->calibration.accel_z_offset;
+ pad->move_data.gyro_x = (gyro_x - dev->calibration.gyro_x_offset) * dev->calibration.gyro_x_gain;
+ pad->move_data.gyro_y = (gyro_y - dev->calibration.gyro_y_offset) * dev->calibration.gyro_y_gain;
+ pad->move_data.gyro_z = (gyro_z - dev->calibration.gyro_z_offset) * dev->calibration.gyro_z_gain;
+ }
+ else
+ {
+ constexpr f32 MOVE_ONE_G = 4096.0f; // This is just a rough estimate and probably depends on the device
+
+ pad->move_data.accelerometer_x = accel_x / MOVE_ONE_G;
+ pad->move_data.accelerometer_y = accel_y / MOVE_ONE_G;
+ pad->move_data.accelerometer_z = accel_z / MOVE_ONE_G;
+ pad->move_data.gyro_x = gyro_x / MOVE_ONE_G;
+ pad->move_data.gyro_y = gyro_y / MOVE_ONE_G;
+ pad->move_data.gyro_z = gyro_z / MOVE_ONE_G;
}
- pad->move_data.accelerometer_x = accel_x / MOVE_ONE_G;
- pad->move_data.accelerometer_y = accel_y / MOVE_ONE_G;
- pad->move_data.accelerometer_z = accel_z / MOVE_ONE_G;
- pad->move_data.gyro_x = accel_x / MOVE_ONE_G;
- pad->move_data.gyro_y = accel_y / MOVE_ONE_G;
- pad->move_data.gyro_z = accel_z / MOVE_ONE_G;
pad->move_data.temperature = ((input.temperature << 4) | ((input.magnetometer_x & 0xF0) >> 4));
pad->m_sensors[0].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_x * -1.0f));
@@ -655,6 +719,58 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding)
pad->m_sensors[2].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_z));
pad->m_sensors[3].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.gyro_z * -1.0f));
+ // Get elapsed time since last update
+ const u64 now_us = get_system_time();
+ const float elapsed_sec = (dev->last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - dev->last_ahrs_update_time_us) / 1'000'000.0f);
+ dev->last_ahrs_update_time_us = now_us;
+
+ // The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
+ // Don't ask how the axis work. It's basically been trial and error.
+ ensure(dev->ahrs.settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up
+
+ const FusionVector accelerometer{
+ .axis {
+ .x = -pad->move_data.accelerometer_x,
+ .y = +pad->move_data.accelerometer_y,
+ .z = +pad->move_data.accelerometer_z
+ }
+ };
+
+ static constexpr f32 PI = 3.14159265f;
+ const auto rad_to_degree = [](f32 radians) -> f32 { return radians * 180.0f / PI; };
+ const FusionVector gyroscope{
+ .axis {
+ .x = +rad_to_degree(pad->move_data.gyro_x),
+ .y = +rad_to_degree(pad->move_data.gyro_z),
+ .z = -rad_to_degree(pad->move_data.gyro_y)
+ }
+ };
+
+ FusionVector magnetometer {};
+
+ // TODO: use magnetometer if possible
+ //if (dev->model == ps_move_model::ZCM1)
+ //{
+ // const ps_move_input_report_ZCM1& input = dev->input_report_ZCM1;
+ // magnetometer = FusionVector{
+ // .axis {
+ // .x = input.magnetometer_x2,
+ // .y = input.magnetometer_y,
+ // .z = input.magnetometer_z
+ // }
+ // };
+ //}
+
+ // Update Fusion
+ FusionAhrsUpdate(&dev->ahrs, gyroscope, accelerometer, magnetometer, elapsed_sec);
+
+ // Get quaternion
+ const FusionQuaternion quaternion = FusionAhrsGetQuaternion(&dev->ahrs);
+ pad->move_data.quaternion[0] = quaternion.array[0];
+ pad->move_data.quaternion[1] = quaternion.array[1];
+ pad->move_data.quaternion[2] = quaternion.array[2];
+ pad->move_data.quaternion[3] = quaternion.array[3];
+
handle_external_device(binding);
}
diff --git a/rpcs3/Input/ps_move_handler.h b/rpcs3/Input/ps_move_handler.h
index edf377e044..434a3c81a2 100644
--- a/rpcs3/Input/ps_move_handler.h
+++ b/rpcs3/Input/ps_move_handler.h
@@ -2,6 +2,15 @@
#include "hid_pad_handler.h"
+#ifndef _MSC_VER
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#endif
+#include "3rdparty/fusion/fusion/Fusion/Fusion.h"
+#ifndef _MSC_VER
+#pragma GCC diagnostic pop
+#endif
+
#include
namespace reports
@@ -80,14 +89,50 @@ namespace reports
{
std::array data{}; // TODO
};
+
+ // Buffer size for calibration data
+ constexpr u32 PSMOVE_CALIBRATION_SIZE = 49;
+
+ // Three blocks, minus header (2 bytes) for blocks 2,3
+ constexpr u32 PSMOVE_ZCM1_CALIBRATION_BLOB_SIZE = PSMOVE_CALIBRATION_SIZE * 3 - 2 * 2;
+
+ // Three blocks, minus header (2 bytes) for block 2
+ constexpr u32 PSMOVE_ZCM2_CALIBRATION_BLOB_SIZE = PSMOVE_CALIBRATION_SIZE * 2 - 2 * 1;
+
+ struct ps_move_calibration_blob
+ {
+ std::array data{};
+ };
}
+enum
+{
+ zero_shift = 0x8000,
+};
+
enum class ps_move_model
{
ZCM1, // PS3
ZCM2, // PS4
};
+struct ps_move_calibration
+{
+ bool is_valid = false;
+ f32 accel_x_factor = 1.0f;
+ f32 accel_y_factor = 1.0f;
+ f32 accel_z_factor = 1.0f;
+ f32 accel_x_offset = 0.0f;
+ f32 accel_y_offset = 0.0f;
+ f32 accel_z_offset = 0.0f;
+ f32 gyro_x_gain = 1.0f;
+ f32 gyro_y_gain = 1.0f;
+ f32 gyro_z_gain = 1.0f;
+ f32 gyro_x_offset = 0.0f;
+ f32 gyro_y_offset = 0.0f;
+ f32 gyro_z_offset = 0.0f;
+};
+
class ps_move_device : public HidDevice
{
public:
@@ -98,6 +143,10 @@ public:
reports::ps_move_output_report last_output_report{};
steady_clock::time_point last_output_report_time;
u32 external_device_id = 0;
+ ps_move_calibration calibration{};
+
+ FusionAhrs ahrs {}; // Used to calculate quaternions from sensor data
+ u64 last_ahrs_update_time_us = 0; // Last ahrs update
const reports::ps_move_input_report_common& input_report_common() const;
};
diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj
index 8c3566dca5..9a196dd8af 100644
--- a/rpcs3/emucore.vcxproj
+++ b/rpcs3/emucore.vcxproj
@@ -40,7 +40,7 @@
Use
- ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib
+ ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion
MaxSpeed
AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;%(PreprocessorDefinitions)
AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;%(PreprocessorDefinitions)
diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index 6f5dafcc08..1973831ab3 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -62,12 +62,12 @@
true
- $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include
+ $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\fusion\fusion\Fusion
$(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)
$(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)
- $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include
+ $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\fusion\fusion\Fusion
@@ -89,7 +89,7 @@
TurnOffAllWarnings
- opencv_world4100.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies)
+ opencv_world4100.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies)
$(SolutionDir)3rdparty\opencv\opencv\opencv410\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib_ext\$(CONFIGURATION)-$(PLATFORM);$(SolutionDir)3rdparty\discord-rpc\lib;$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories)
"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)
true
@@ -141,7 +141,7 @@
$(IntDir)vc$(PlatformToolsetVersion).pdb
- opencv_world4100.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies)
+ opencv_world4100.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies)
$(SolutionDir)3rdparty\opencv\opencv\opencv410\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)3rdparty\discord-rpc\lib;$(SolutionDir)build\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories)
"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions)
true
@@ -179,6 +179,7 @@
+
@@ -971,6 +972,7 @@
$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
+
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index b7e277ca51..0e2eae3a00 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -1143,6 +1143,9 @@
Io\Move
+
+ Io\Move
+
@@ -1343,6 +1346,9 @@
Io\Move
+
+ Io\Move
+
diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt
index 2629da0880..e3e3c0a264 100644
--- a/rpcs3/rpcs3qt/CMakeLists.txt
+++ b/rpcs3/rpcs3qt/CMakeLists.txt
@@ -160,4 +160,5 @@ target_link_libraries(rpcs3_ui
3rdparty::wolfssl
3rdparty::libcurl
3rdparty::opencv
+ 3rdparty::fusion
3rdparty::rtmidi)