Merge pull request #7952 from jordan-woyak/emu-shake-params
WiimoteEmu: Allow shake frequency and intensity to be configured.
This commit is contained in:
commit
a891115ea3
|
@ -30,7 +30,6 @@ add_library(core
|
||||||
Config/NetplaySettings.cpp
|
Config/NetplaySettings.cpp
|
||||||
Config/SYSCONFSettings.cpp
|
Config/SYSCONFSettings.cpp
|
||||||
Config/UISettings.cpp
|
Config/UISettings.cpp
|
||||||
Config/WiimoteInputSettings.cpp
|
|
||||||
ConfigLoaders/BaseConfigLoader.cpp
|
ConfigLoaders/BaseConfigLoader.cpp
|
||||||
ConfigLoaders/GameConfigLoader.cpp
|
ConfigLoaders/GameConfigLoader.cpp
|
||||||
ConfigLoaders/IsSettingSaveable.cpp
|
ConfigLoaders/IsSettingSaveable.cpp
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2018 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/Config/WiimoteInputSettings.h"
|
|
||||||
|
|
||||||
namespace Config
|
|
||||||
{
|
|
||||||
// Configuration Information
|
|
||||||
|
|
||||||
// WiimoteInput.Settings
|
|
||||||
|
|
||||||
const ConfigInfo<double> WIIMOTE_INPUT_SHAKE_INTENSITY_HARD{{System::WiiPad, "Shake", "Hard"}, 5.0};
|
|
||||||
const ConfigInfo<double> WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM{{System::WiiPad, "Shake", "Medium"},
|
|
||||||
3.0};
|
|
||||||
const ConfigInfo<double> WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT{{System::WiiPad, "Shake", "Soft"}, 2.0};
|
|
||||||
|
|
||||||
// Dynamic settings
|
|
||||||
const ConfigInfo<int> WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD{
|
|
||||||
{System::WiiPad, "Dynamic_Shake", "FramesHeldHard"}, 45};
|
|
||||||
const ConfigInfo<int> WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT{
|
|
||||||
{System::WiiPad, "Dynamic_Shake", "FramesHeldSoft"}, 15};
|
|
||||||
const ConfigInfo<int> WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH{
|
|
||||||
{System::WiiPad, "Dynamic_Shake", "FrameCount"}, 30};
|
|
||||||
|
|
||||||
// NunchuckInput.Settings
|
|
||||||
const ConfigInfo<double> NUNCHUK_INPUT_SHAKE_INTENSITY_HARD{
|
|
||||||
{System::WiiPad, "Nunchuk_Shake", "Hard"}, 5.0};
|
|
||||||
const ConfigInfo<double> NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM{
|
|
||||||
{System::WiiPad, "Nunchuk_Shake", "Medium"}, 3.0};
|
|
||||||
const ConfigInfo<double> NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT{
|
|
||||||
{System::WiiPad, "Nunchuk_Shake", "Soft"}, 2.0};
|
|
||||||
} // namespace Config
|
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright 2018 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Common/Config/Config.h"
|
|
||||||
|
|
||||||
namespace Config
|
|
||||||
{
|
|
||||||
// Configuration Information
|
|
||||||
|
|
||||||
// WiimoteInput.Settings
|
|
||||||
extern const ConfigInfo<double> WIIMOTE_INPUT_SHAKE_INTENSITY_HARD;
|
|
||||||
extern const ConfigInfo<double> WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM;
|
|
||||||
extern const ConfigInfo<double> WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT;
|
|
||||||
|
|
||||||
// Below settings are for dynamic input only (based on how long the user holds a button)
|
|
||||||
extern const ConfigInfo<int>
|
|
||||||
WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD; // How long button held constitutes a hard shake
|
|
||||||
extern const ConfigInfo<int>
|
|
||||||
WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT; // How long button held constitutes a soft shake
|
|
||||||
extern const ConfigInfo<int>
|
|
||||||
WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH; // How long to execute a shake
|
|
||||||
|
|
||||||
// NunchuckInput.Settings
|
|
||||||
extern const ConfigInfo<double> NUNCHUK_INPUT_SHAKE_INTENSITY_HARD;
|
|
||||||
extern const ConfigInfo<double> NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM;
|
|
||||||
extern const ConfigInfo<double> NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT;
|
|
||||||
|
|
||||||
} // namespace Config
|
|
|
@ -56,7 +56,6 @@
|
||||||
<ClCompile Include="ConfigLoaders\MovieConfigLoader.cpp" />
|
<ClCompile Include="ConfigLoaders\MovieConfigLoader.cpp" />
|
||||||
<ClCompile Include="ConfigLoaders\NetPlayConfigLoader.cpp" />
|
<ClCompile Include="ConfigLoaders\NetPlayConfigLoader.cpp" />
|
||||||
<ClCompile Include="ConfigManager.cpp" />
|
<ClCompile Include="ConfigManager.cpp" />
|
||||||
<ClCompile Include="Config\WiimoteInputSettings.cpp" />
|
|
||||||
<ClCompile Include="Core.cpp" />
|
<ClCompile Include="Core.cpp" />
|
||||||
<ClCompile Include="CoreTiming.cpp" />
|
<ClCompile Include="CoreTiming.cpp" />
|
||||||
<ClCompile Include="Debugger\Debugger_SymbolMap.cpp" />
|
<ClCompile Include="Debugger\Debugger_SymbolMap.cpp" />
|
||||||
|
@ -327,7 +326,6 @@
|
||||||
<ClInclude Include="ConfigLoaders\NetPlayConfigLoader.h" />
|
<ClInclude Include="ConfigLoaders\NetPlayConfigLoader.h" />
|
||||||
<ClInclude Include="ConfigManager.h" />
|
<ClInclude Include="ConfigManager.h" />
|
||||||
<ClInclude Include="Config\UISettings.h" />
|
<ClInclude Include="Config\UISettings.h" />
|
||||||
<ClInclude Include="Config\WiimoteInputSettings.h" />
|
|
||||||
<ClInclude Include="Core.h" />
|
<ClInclude Include="Core.h" />
|
||||||
<ClInclude Include="CoreTiming.h" />
|
<ClInclude Include="CoreTiming.h" />
|
||||||
<ClInclude Include="Debugger\Debugger_SymbolMap.h" />
|
<ClInclude Include="Debugger\Debugger_SymbolMap.h" />
|
||||||
|
|
|
@ -874,9 +874,6 @@
|
||||||
<ClCompile Include="Config\UISettings.cpp">
|
<ClCompile Include="Config\UISettings.cpp">
|
||||||
<Filter>Config</Filter>
|
<Filter>Config</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Config\WiimoteInputSettings.cpp">
|
|
||||||
<Filter>Config</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\Jit64\RegCache\FPURegCache.cpp" />
|
<ClCompile Include="PowerPC\Jit64\RegCache\FPURegCache.cpp" />
|
||||||
<ClCompile Include="PowerPC\Jit64\RegCache\GPRRegCache.cpp" />
|
<ClCompile Include="PowerPC\Jit64\RegCache\GPRRegCache.cpp" />
|
||||||
<ClCompile Include="PowerPC\Jit64\RegCache\JitRegCache.cpp" />
|
<ClCompile Include="PowerPC\Jit64\RegCache\JitRegCache.cpp" />
|
||||||
|
@ -1592,9 +1589,6 @@
|
||||||
<ClInclude Include="Config\UISettings.h">
|
<ClInclude Include="Config\UISettings.h">
|
||||||
<Filter>Config</Filter>
|
<Filter>Config</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="Config\WiimoteInputSettings.h">
|
|
||||||
<Filter>Config</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="HW\WiimoteEmu\I2CBus.h">
|
<ClInclude Include="HW\WiimoteEmu\I2CBus.h">
|
||||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -1638,4 +1632,4 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Text Include="CMakeLists.txt" />
|
<Text Include="CMakeLists.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
#include "Common/MathUtil.h"
|
#include "Common/MathUtil.h"
|
||||||
#include "Core/Config/SYSCONFSettings.h"
|
#include "Core/Config/SYSCONFSettings.h"
|
||||||
#include "Core/Config/WiimoteInputSettings.h"
|
|
||||||
#include "Core/HW/Wiimote.h"
|
|
||||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||||
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
|
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
|
||||||
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
|
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
|
||||||
|
@ -18,11 +16,6 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
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;
|
|
||||||
|
|
||||||
// Given a velocity, acceleration, and maximum jerk value,
|
// Given a velocity, acceleration, and maximum jerk value,
|
||||||
// calculate change in position after a stop in the shortest possible time.
|
// calculate change in position after a stop in the shortest possible time.
|
||||||
// Used to smoothly adjust acceleration and come to complete stops at precise positions.
|
// Used to smoothly adjust acceleration and come to complete stops at precise positions.
|
||||||
|
@ -61,81 +54,32 @@ double CalculateStopDistance(double velocity, double max_accel)
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
Common::Vec3 EmulateShake(ControllerEmu::Buttons* const buttons_group, const double intensity,
|
void EmulateShake(PositionalState* state, ControllerEmu::Shake* const shake_group,
|
||||||
u8* const shake_step)
|
float time_elapsed)
|
||||||
{
|
{
|
||||||
// shake is a bitfield of X,Y,Z shake button states
|
auto target_position = shake_group->GetState() * shake_group->GetIntensity() / 2;
|
||||||
static const unsigned int btns[] = {0x01, 0x02, 0x04};
|
for (std::size_t i = 0; i != target_position.data.size(); ++i)
|
||||||
unsigned int shake = 0;
|
|
||||||
buttons_group->GetState(&shake, btns);
|
|
||||||
|
|
||||||
Common::Vec3 accel;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i != accel.data.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if (shake & (1 << i))
|
if (state->velocity.data[i] * std::copysign(1.f, target_position.data[i]) < 0 ||
|
||||||
|
state->position.data[i] / target_position.data[i] > 0.5)
|
||||||
{
|
{
|
||||||
accel.data[i] = std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity *
|
target_position.data[i] *= -1;
|
||||||
GRAVITY_ACCELERATION;
|
|
||||||
shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shake_step[i] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return accel;
|
// Time from "top" to "bottom" of one shake.
|
||||||
}
|
const auto travel_time = 1 / shake_group->GetFrequency() / 2;
|
||||||
|
|
||||||
Common::Vec3 EmulateDynamicShake(DynamicData& dynamic_data,
|
Common::Vec3 jerk;
|
||||||
ControllerEmu::Buttons* const buttons_group,
|
for (std::size_t i = 0; i != target_position.data.size(); ++i)
|
||||||
const DynamicConfiguration& config, u8* const shake_step)
|
|
||||||
{
|
|
||||||
// shake is a bitfield of X,Y,Z shake button states
|
|
||||||
static const unsigned int btns[] = {0x01, 0x02, 0x04};
|
|
||||||
unsigned int shake = 0;
|
|
||||||
buttons_group->GetState(&shake, btns);
|
|
||||||
|
|
||||||
Common::Vec3 accel;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i != accel.data.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if ((shake & (1 << i)) && dynamic_data.executing_frames_left[i] == 0)
|
const auto half_distance =
|
||||||
{
|
std::max(std::abs(target_position.data[i]), std::abs(state->position.data[i]));
|
||||||
dynamic_data.timing[i]++;
|
|
||||||
}
|
jerk.data[i] = half_distance / std::pow(travel_time / 2, 3);
|
||||||
else if (dynamic_data.executing_frames_left[i] > 0)
|
|
||||||
{
|
|
||||||
accel.data[i] = std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) *
|
|
||||||
dynamic_data.intensity[i] * GRAVITY_ACCELERATION;
|
|
||||||
shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX;
|
|
||||||
dynamic_data.executing_frames_left[i]--;
|
|
||||||
}
|
|
||||||
else if (shake == 0 && dynamic_data.timing[i] > 0)
|
|
||||||
{
|
|
||||||
if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity)
|
|
||||||
{
|
|
||||||
dynamic_data.intensity[i] = config.high_intensity;
|
|
||||||
}
|
|
||||||
else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity)
|
|
||||||
{
|
|
||||||
dynamic_data.intensity[i] = config.low_intensity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dynamic_data.intensity[i] = config.med_intensity;
|
|
||||||
}
|
|
||||||
dynamic_data.timing[i] = 0;
|
|
||||||
dynamic_data.executing_frames_left[i] = config.frames_to_execute;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shake_step[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return accel;
|
ApproachPositionWithJerk(state, target_position, jerk, time_elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* const tilt_group, float time_elapsed)
|
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* const tilt_group, float time_elapsed)
|
||||||
|
@ -158,8 +102,8 @@ void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float t
|
||||||
|
|
||||||
// Note. Y/Z swapped because X/Y axis to the swing_group is X/Z to the wiimote.
|
// Note. Y/Z swapped because X/Y axis to the swing_group is X/Z to the wiimote.
|
||||||
// X is negated because Wiimote X+ is to the left.
|
// X is negated because Wiimote X+ is to the left.
|
||||||
ApproachPositionWithJerk(state, {-target.x, -target.z, target.y}, swing_group->GetMaxJerk(),
|
ApproachPositionWithJerk(state, {-target.x, -target.z, target.y},
|
||||||
time_elapsed);
|
Common::Vec3{1, 1, 1} * swing_group->GetMaxJerk(), time_elapsed);
|
||||||
|
|
||||||
// Just jump to our target angle scaled by our progress to the target position.
|
// Just jump to our target angle scaled by our progress to the target position.
|
||||||
// TODO: If we wanted to be less hacky we could use ApproachAngleWithAccel.
|
// TODO: If we wanted to be less hacky we could use ApproachAngleWithAccel.
|
||||||
|
@ -248,19 +192,19 @@ void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& angle_ta
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApproachPositionWithJerk(PositionalState* state, const Common::Vec3& position_target,
|
void ApproachPositionWithJerk(PositionalState* state, const Common::Vec3& position_target,
|
||||||
float max_jerk, float time_elapsed)
|
const Common::Vec3& max_jerk, float time_elapsed)
|
||||||
{
|
{
|
||||||
const auto stop_distance =
|
const auto stop_distance =
|
||||||
Common::Vec3(CalculateStopDistance(state->velocity.x, state->acceleration.x, max_jerk),
|
Common::Vec3(CalculateStopDistance(state->velocity.x, state->acceleration.x, max_jerk.x),
|
||||||
CalculateStopDistance(state->velocity.y, state->acceleration.y, max_jerk),
|
CalculateStopDistance(state->velocity.y, state->acceleration.y, max_jerk.y),
|
||||||
CalculateStopDistance(state->velocity.z, state->acceleration.z, max_jerk));
|
CalculateStopDistance(state->velocity.z, state->acceleration.z, max_jerk.z));
|
||||||
|
|
||||||
const auto offset = position_target - state->position;
|
const auto offset = position_target - state->position;
|
||||||
const auto stop_offset = offset - stop_distance;
|
const auto stop_offset = offset - stop_distance;
|
||||||
|
|
||||||
const Common::Vec3 jerk{std::copysign(max_jerk, stop_offset.x),
|
const Common::Vec3 jerk{std::copysign(max_jerk.x, stop_offset.x),
|
||||||
std::copysign(max_jerk, stop_offset.y),
|
std::copysign(max_jerk.y, stop_offset.y),
|
||||||
std::copysign(max_jerk, stop_offset.z)};
|
std::copysign(max_jerk.z, stop_offset.z)};
|
||||||
|
|
||||||
state->acceleration += jerk * time_elapsed;
|
state->acceleration += jerk * time_elapsed;
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,6 @@ namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
constexpr double GRAVITY_ACCELERATION = 9.80665;
|
constexpr double GRAVITY_ACCELERATION = 9.80665;
|
||||||
|
|
||||||
// Used for dynamic shake
|
|
||||||
// TODO: kill this.
|
|
||||||
struct DynamicData
|
|
||||||
{
|
|
||||||
std::array<int, 3> timing; // Hold length in frames for each axis
|
|
||||||
std::array<double, 3> intensity; // Swing or shake intensity
|
|
||||||
std::array<int, 3> executing_frames_left; // Number of frames to execute the intensity operation
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PositionalState
|
struct PositionalState
|
||||||
{
|
{
|
||||||
Common::Vec3 position;
|
Common::Vec3 position;
|
||||||
|
@ -47,34 +38,13 @@ struct MotionState : PositionalState, RotationalState
|
||||||
// Build a rotational matrix from euler angles.
|
// Build a rotational matrix from euler angles.
|
||||||
Common::Matrix33 GetRotationalMatrix(const Common::Vec3& angle);
|
Common::Matrix33 GetRotationalMatrix(const Common::Vec3& angle);
|
||||||
|
|
||||||
void ApproachPositionWithJerk(PositionalState* state, const Common::Vec3& target, float max_jerk,
|
void ApproachPositionWithJerk(PositionalState* state, const Common::Vec3& target,
|
||||||
float time_elapsed);
|
const Common::Vec3& max_jerk, float time_elapsed);
|
||||||
|
|
||||||
void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& target, float max_accel,
|
void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& target, float max_accel,
|
||||||
float time_elapsed);
|
float time_elapsed);
|
||||||
|
|
||||||
// Used for dynamic shake.
|
void EmulateShake(PositionalState* state, ControllerEmu::Shake* shake_group, float time_elapsed);
|
||||||
// This is used to pass in data that defines the dynamic action
|
|
||||||
// TODO: kill this
|
|
||||||
struct DynamicConfiguration
|
|
||||||
{
|
|
||||||
double low_intensity;
|
|
||||||
int frames_needed_for_low_intensity;
|
|
||||||
|
|
||||||
double med_intensity;
|
|
||||||
// Frames needed for med intensity can be calculated between high & low
|
|
||||||
|
|
||||||
double high_intensity;
|
|
||||||
int frames_needed_for_high_intensity;
|
|
||||||
|
|
||||||
int frames_to_execute; // How many frames should we execute the action for?
|
|
||||||
};
|
|
||||||
|
|
||||||
Common::Vec3 EmulateShake(ControllerEmu::Buttons* buttons_group, double intensity, u8* shake_step);
|
|
||||||
|
|
||||||
Common::Vec3 EmulateDynamicShake(DynamicData& dynamic_data, ControllerEmu::Buttons* buttons_group,
|
|
||||||
const DynamicConfiguration& config, u8* shake_step);
|
|
||||||
|
|
||||||
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* tilt_group, float time_elapsed);
|
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* tilt_group, float time_elapsed);
|
||||||
void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float time_elapsed);
|
void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float time_elapsed);
|
||||||
|
|
||||||
|
|
|
@ -584,9 +584,7 @@ void Wiimote::DoState(PointerWrap& p)
|
||||||
// Dynamics
|
// Dynamics
|
||||||
p.Do(m_swing_state);
|
p.Do(m_swing_state);
|
||||||
p.Do(m_tilt_state);
|
p.Do(m_tilt_state);
|
||||||
|
p.Do(m_shake_state);
|
||||||
// TODO: clean this up:
|
|
||||||
p.Do(m_shake_step);
|
|
||||||
|
|
||||||
p.DoMarker("Wiimote");
|
p.DoMarker("Wiimote");
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/MathUtil.h"
|
#include "Common/MathUtil.h"
|
||||||
#include "Core/Config/WiimoteInputSettings.h"
|
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||||
|
|
||||||
|
@ -50,24 +49,10 @@ Nunchuk::Nunchuk() : EncryptedExtension(_trans("Nunchuk"))
|
||||||
// tilt
|
// tilt
|
||||||
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));
|
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));
|
||||||
|
|
||||||
// shake
|
// Shake
|
||||||
groups.emplace_back(m_shake = new ControllerEmu::Buttons(_trans("Shake")));
|
// Inverse the default intensity so shake is opposite that of wiimote.
|
||||||
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
// This is needed by DKCR for proper shake action detection.
|
||||||
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("X")));
|
groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake"), -1));
|
||||||
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
|
||||||
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Y")));
|
|
||||||
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
|
||||||
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z")));
|
|
||||||
|
|
||||||
groups.emplace_back(m_shake_soft = new ControllerEmu::Buttons("ShakeSoft"));
|
|
||||||
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
|
|
||||||
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
|
|
||||||
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
|
|
||||||
|
|
||||||
groups.emplace_back(m_shake_hard = new ControllerEmu::Buttons("ShakeHard"));
|
|
||||||
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
|
|
||||||
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
|
|
||||||
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nunchuk::Update()
|
void Nunchuk::Update()
|
||||||
|
@ -104,6 +89,7 @@ void Nunchuk::Update()
|
||||||
// Acceleration data:
|
// Acceleration data:
|
||||||
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
|
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
|
||||||
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
|
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
|
||||||
|
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);
|
||||||
|
|
||||||
const auto transformation =
|
const auto transformation =
|
||||||
GetRotationalMatrix(-m_tilt_state.angle) * GetRotationalMatrix(-m_swing_state.angle);
|
GetRotationalMatrix(-m_tilt_state.angle) * GetRotationalMatrix(-m_swing_state.angle);
|
||||||
|
@ -112,12 +98,7 @@ void Nunchuk::Update()
|
||||||
Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)));
|
Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)));
|
||||||
|
|
||||||
// shake
|
// shake
|
||||||
accel += EmulateShake(m_shake, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM),
|
accel += m_shake_state.acceleration;
|
||||||
m_shake_step.data());
|
|
||||||
accel += EmulateShake(m_shake_soft, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT),
|
|
||||||
m_shake_soft_step.data());
|
|
||||||
accel += EmulateShake(m_shake_hard, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_HARD),
|
|
||||||
m_shake_hard_step.data());
|
|
||||||
|
|
||||||
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
|
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
|
||||||
const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
|
const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
|
||||||
|
@ -203,6 +184,7 @@ void Nunchuk::DoState(PointerWrap& p)
|
||||||
|
|
||||||
p.Do(m_swing_state);
|
p.Do(m_swing_state);
|
||||||
p.Do(m_tilt_state);
|
p.Do(m_tilt_state);
|
||||||
|
p.Do(m_shake_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
|
void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
|
||||||
|
@ -227,5 +209,9 @@ void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
|
||||||
m_buttons->SetControlExpression(0, "Control_L"); // C
|
m_buttons->SetControlExpression(0, "Control_L"); // C
|
||||||
m_buttons->SetControlExpression(1, "Shift_L"); // Z
|
m_buttons->SetControlExpression(1, "Shift_L"); // Z
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Shake
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
m_shake->SetControlExpression(i, "Click 2");
|
||||||
}
|
}
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -101,23 +101,14 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ControllerEmu::Tilt* m_tilt;
|
ControllerEmu::Tilt* m_tilt;
|
||||||
|
|
||||||
ControllerEmu::Force* m_swing;
|
ControllerEmu::Force* m_swing;
|
||||||
|
ControllerEmu::Shake* m_shake;
|
||||||
ControllerEmu::Buttons* m_shake;
|
|
||||||
ControllerEmu::Buttons* m_shake_soft;
|
|
||||||
ControllerEmu::Buttons* m_shake_hard;
|
|
||||||
|
|
||||||
ControllerEmu::Buttons* m_buttons;
|
ControllerEmu::Buttons* m_buttons;
|
||||||
ControllerEmu::AnalogStick* m_stick;
|
ControllerEmu::AnalogStick* m_stick;
|
||||||
|
|
||||||
// Dynamics:
|
// Dynamics:
|
||||||
MotionState m_swing_state;
|
MotionState m_swing_state;
|
||||||
RotationalState m_tilt_state;
|
RotationalState m_tilt_state;
|
||||||
|
PositionalState m_shake_state;
|
||||||
// TODO: kill
|
|
||||||
std::array<u8, 3> m_shake_step{};
|
|
||||||
std::array<u8, 3> m_shake_soft_step{};
|
|
||||||
std::array<u8, 3> m_shake_hard_step{};
|
|
||||||
};
|
};
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
|
||||||
#include "Core/Config/SYSCONFSettings.h"
|
#include "Core/Config/SYSCONFSettings.h"
|
||||||
#include "Core/Config/WiimoteInputSettings.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
|
@ -130,11 +129,7 @@ void Wiimote::Reset()
|
||||||
// Dynamics:
|
// Dynamics:
|
||||||
m_swing_state = {};
|
m_swing_state = {};
|
||||||
m_tilt_state = {};
|
m_tilt_state = {};
|
||||||
|
m_shake_state = {};
|
||||||
m_shake_step = {};
|
|
||||||
m_shake_soft_step = {};
|
|
||||||
m_shake_hard_step = {};
|
|
||||||
m_shake_dynamic_data = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Wiimote::Wiimote(const unsigned int index) : m_index(index)
|
Wiimote::Wiimote(const unsigned int index) : m_index(index)
|
||||||
|
@ -159,31 +154,7 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index)
|
||||||
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));
|
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));
|
||||||
|
|
||||||
// shake
|
// shake
|
||||||
groups.emplace_back(m_shake = new ControllerEmu::Buttons(_trans("Shake")));
|
groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake")));
|
||||||
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
|
||||||
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("X")));
|
|
||||||
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
|
||||||
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Y")));
|
|
||||||
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
|
||||||
m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z")));
|
|
||||||
|
|
||||||
groups.emplace_back(m_shake_soft = new ControllerEmu::Buttons("ShakeSoft"));
|
|
||||||
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
|
|
||||||
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
|
|
||||||
m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
|
|
||||||
|
|
||||||
groups.emplace_back(m_shake_hard = new ControllerEmu::Buttons("ShakeHard"));
|
|
||||||
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
|
|
||||||
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
|
|
||||||
m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
|
|
||||||
|
|
||||||
groups.emplace_back(m_shake_dynamic = new ControllerEmu::Buttons("Shake Dynamic"));
|
|
||||||
m_shake_dynamic->controls.emplace_back(
|
|
||||||
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X"));
|
|
||||||
m_shake_dynamic->controls.emplace_back(
|
|
||||||
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y"));
|
|
||||||
m_shake_dynamic->controls.emplace_back(
|
|
||||||
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
|
|
||||||
|
|
||||||
// extension
|
// extension
|
||||||
groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension")));
|
groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension")));
|
||||||
|
@ -687,6 +658,7 @@ void Wiimote::StepDynamics()
|
||||||
{
|
{
|
||||||
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
|
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
|
||||||
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
|
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
|
||||||
|
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);
|
||||||
|
|
||||||
// TODO: Move cursor state out of ControllerEmu::Cursor
|
// TODO: Move cursor state out of ControllerEmu::Cursor
|
||||||
// const auto cursor_mtx = EmulateCursorMovement(m_ir);
|
// const auto cursor_mtx = EmulateCursorMovement(m_ir);
|
||||||
|
@ -710,24 +682,7 @@ Common::Vec3 Wiimote::GetAcceleration()
|
||||||
GetTransformation().Transform(
|
GetTransformation().Transform(
|
||||||
m_swing_state.acceleration + Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)), 0);
|
m_swing_state.acceleration + Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)), 0);
|
||||||
|
|
||||||
DynamicConfiguration shake_config;
|
accel += m_shake_state.acceleration;
|
||||||
shake_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT);
|
|
||||||
shake_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM);
|
|
||||||
shake_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD);
|
|
||||||
shake_config.frames_needed_for_high_intensity =
|
|
||||||
Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD);
|
|
||||||
shake_config.frames_needed_for_low_intensity =
|
|
||||||
Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT);
|
|
||||||
shake_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH);
|
|
||||||
|
|
||||||
accel += EmulateShake(m_shake, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM),
|
|
||||||
m_shake_step.data());
|
|
||||||
accel += EmulateShake(m_shake_soft, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT),
|
|
||||||
m_shake_soft_step.data());
|
|
||||||
accel += EmulateShake(m_shake_hard, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD),
|
|
||||||
m_shake_hard_step.data());
|
|
||||||
accel += EmulateDynamicShake(m_shake_dynamic_data, m_shake_dynamic, shake_config,
|
|
||||||
m_shake_dynamic_step.data());
|
|
||||||
|
|
||||||
return accel;
|
return accel;
|
||||||
}
|
}
|
||||||
|
@ -735,9 +690,10 @@ Common::Vec3 Wiimote::GetAcceleration()
|
||||||
Common::Matrix44 Wiimote::GetTransformation() const
|
Common::Matrix44 Wiimote::GetTransformation() const
|
||||||
{
|
{
|
||||||
// Includes positional and rotational effects of:
|
// Includes positional and rotational effects of:
|
||||||
// IR, Swing, Tilt
|
// IR, Swing, Tilt, Shake
|
||||||
|
|
||||||
return Common::Matrix44::FromMatrix33(GetRotationalMatrix(-m_tilt_state.angle) *
|
return Common::Matrix44::Translate(-m_shake_state.position) *
|
||||||
|
Common::Matrix44::FromMatrix33(GetRotationalMatrix(-m_tilt_state.angle) *
|
||||||
GetRotationalMatrix(-m_swing_state.angle)) *
|
GetRotationalMatrix(-m_swing_state.angle)) *
|
||||||
EmulateCursorMovement(m_ir) * Common::Matrix44::Translate(-m_swing_state.position);
|
EmulateCursorMovement(m_ir) * Common::Matrix44::Translate(-m_swing_state.position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,10 +222,7 @@ private:
|
||||||
// Control groups for user input:
|
// Control groups for user input:
|
||||||
ControllerEmu::Buttons* m_buttons;
|
ControllerEmu::Buttons* m_buttons;
|
||||||
ControllerEmu::Buttons* m_dpad;
|
ControllerEmu::Buttons* m_dpad;
|
||||||
ControllerEmu::Buttons* m_shake;
|
ControllerEmu::Shake* m_shake;
|
||||||
ControllerEmu::Buttons* m_shake_soft;
|
|
||||||
ControllerEmu::Buttons* m_shake_hard;
|
|
||||||
ControllerEmu::Buttons* m_shake_dynamic;
|
|
||||||
ControllerEmu::Cursor* m_ir;
|
ControllerEmu::Cursor* m_ir;
|
||||||
ControllerEmu::Tilt* m_tilt;
|
ControllerEmu::Tilt* m_tilt;
|
||||||
ControllerEmu::Force* m_swing;
|
ControllerEmu::Force* m_swing;
|
||||||
|
@ -270,12 +267,6 @@ private:
|
||||||
// Dynamics:
|
// Dynamics:
|
||||||
MotionState m_swing_state;
|
MotionState m_swing_state;
|
||||||
RotationalState m_tilt_state;
|
RotationalState m_tilt_state;
|
||||||
|
PositionalState m_shake_state;
|
||||||
// TODO: kill these:
|
|
||||||
std::array<u8, 3> m_shake_step{};
|
|
||||||
std::array<u8, 3> m_shake_soft_step{};
|
|
||||||
std::array<u8, 3> m_shake_hard_step{};
|
|
||||||
std::array<u8, 3> m_shake_dynamic_step{};
|
|
||||||
DynamicData m_shake_dynamic_data;
|
|
||||||
};
|
};
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
static const u32 STATE_VERSION = 106; // Last changed in PR 7984
|
static const u32 STATE_VERSION = 107; // Last changed in PR 7952
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
|
|
@ -514,6 +514,95 @@ void MappingIndicator::paintEvent(QPaintEvent*)
|
||||||
Settings::Instance().SetControllerStateNeeded(false);
|
Settings::Instance().SetControllerStateNeeded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShakeMappingIndicator::ShakeMappingIndicator(ControllerEmu::Shake* group)
|
||||||
|
: MappingIndicator(group), m_shake_group(*group)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShakeMappingIndicator::paintEvent(QPaintEvent*)
|
||||||
|
{
|
||||||
|
Settings::Instance().SetControllerStateNeeded(true);
|
||||||
|
DrawShake();
|
||||||
|
Settings::Instance().SetControllerStateNeeded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShakeMappingIndicator::DrawShake()
|
||||||
|
{
|
||||||
|
constexpr std::size_t HISTORY_COUNT = INDICATOR_UPDATE_FREQ;
|
||||||
|
|
||||||
|
WiimoteEmu::EmulateShake(&m_motion_state, &m_shake_group, 1.f / INDICATOR_UPDATE_FREQ);
|
||||||
|
|
||||||
|
constexpr float MAX_DISTANCE = 0.5f;
|
||||||
|
|
||||||
|
m_position_samples.push_front(m_motion_state.position / MAX_DISTANCE);
|
||||||
|
// This also holds the current state so +1.
|
||||||
|
if (m_position_samples.size() > HISTORY_COUNT + 1)
|
||||||
|
m_position_samples.pop_back();
|
||||||
|
|
||||||
|
// Bounding box size:
|
||||||
|
const double scale = height() / 2.5;
|
||||||
|
|
||||||
|
QPainter p(this);
|
||||||
|
p.translate(width() / 2, height() / 2);
|
||||||
|
|
||||||
|
// Bounding box.
|
||||||
|
p.setBrush(BBOX_BRUSH_COLOR);
|
||||||
|
p.setPen(BBOX_PEN_COLOR);
|
||||||
|
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);
|
||||||
|
|
||||||
|
// UI y-axis is opposite that of acceleration Z.
|
||||||
|
p.scale(1.0, -1.0);
|
||||||
|
|
||||||
|
// Enable AA after drawing bounding box.
|
||||||
|
p.setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||||
|
|
||||||
|
// Deadzone.
|
||||||
|
p.setPen(DEADZONE_COLOR);
|
||||||
|
p.setBrush(DEADZONE_BRUSH);
|
||||||
|
p.drawRect(-scale, 0, scale * 2, m_shake_group.GetDeadzone() * scale);
|
||||||
|
|
||||||
|
// Raw input.
|
||||||
|
const auto raw_coord = m_shake_group.GetState(false);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(RAW_INPUT_COLOR);
|
||||||
|
for (std::size_t c = 0; c != raw_coord.data.size(); ++c)
|
||||||
|
{
|
||||||
|
p.drawEllipse(QPointF{-0.5 + c * 0.5, raw_coord.data[c]} * scale, INPUT_DOT_RADIUS,
|
||||||
|
INPUT_DOT_RADIUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid line.
|
||||||
|
if (m_grid_line_position ||
|
||||||
|
std::any_of(m_position_samples.begin(), m_position_samples.end(),
|
||||||
|
[](const Common::Vec3& v) { return v.LengthSquared() != 0.0; }))
|
||||||
|
{
|
||||||
|
// Only start moving the line if there's non-zero data.
|
||||||
|
m_grid_line_position = (m_grid_line_position + 1) % HISTORY_COUNT;
|
||||||
|
}
|
||||||
|
const double grid_line_x = 1.0 - m_grid_line_position * 2.0 / HISTORY_COUNT;
|
||||||
|
p.setPen(RAW_INPUT_COLOR);
|
||||||
|
p.drawLine(QPointF{grid_line_x, -1.0} * scale, QPointF{grid_line_x, 1.0} * scale);
|
||||||
|
|
||||||
|
// Position history.
|
||||||
|
const QColor component_colors[] = {Qt::red, Qt::green, Qt::blue};
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
for (std::size_t c = 0; c != raw_coord.data.size(); ++c)
|
||||||
|
{
|
||||||
|
QPolygonF polyline;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (auto& sample : m_position_samples)
|
||||||
|
{
|
||||||
|
polyline.append(QPointF{1.0 - i * 2.0 / HISTORY_COUNT, sample.data[c]} * scale);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.setPen(component_colors[c]);
|
||||||
|
p.drawPolyline(polyline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MappingIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
|
void MappingIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
|
||||||
{
|
{
|
||||||
// TODO: Ugly magic number used in a few places in this file.
|
// TODO: Ugly magic number used in a few places in this file.
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||||
#include "InputCommon/ControllerEmu/StickGate.h"
|
#include "InputCommon/ControllerEmu/StickGate.h"
|
||||||
|
|
||||||
|
@ -31,6 +33,9 @@ public:
|
||||||
|
|
||||||
void SetCalibrationWidget(CalibrationWidget* widget);
|
void SetCalibrationWidget(CalibrationWidget* widget);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WiimoteEmu::MotionState m_motion_state{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawCursor(ControllerEmu::Cursor& cursor);
|
void DrawCursor(ControllerEmu::Cursor& cursor);
|
||||||
void DrawReshapableInput(ControllerEmu::ReshapableInput& stick);
|
void DrawReshapableInput(ControllerEmu::ReshapableInput& stick);
|
||||||
|
@ -45,8 +50,21 @@ private:
|
||||||
|
|
||||||
ControllerEmu::ControlGroup* const m_group;
|
ControllerEmu::ControlGroup* const m_group;
|
||||||
CalibrationWidget* m_calibration_widget{};
|
CalibrationWidget* m_calibration_widget{};
|
||||||
|
};
|
||||||
|
|
||||||
WiimoteEmu::MotionState m_motion_state{};
|
class ShakeMappingIndicator : public MappingIndicator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ShakeMappingIndicator(ControllerEmu::Shake* group);
|
||||||
|
|
||||||
|
void DrawShake();
|
||||||
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<ControllerEmu::Shake::StateData> m_position_samples;
|
||||||
|
int m_grid_line_position = 0;
|
||||||
|
|
||||||
|
ControllerEmu::Shake& m_shake_group;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CalibrationWidget : public QToolButton
|
class CalibrationWidget : public QToolButton
|
||||||
|
|
|
@ -85,7 +85,8 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
|
||||||
group->type == ControllerEmu::GroupType::Stick ||
|
group->type == ControllerEmu::GroupType::Stick ||
|
||||||
group->type == ControllerEmu::GroupType::Tilt ||
|
group->type == ControllerEmu::GroupType::Tilt ||
|
||||||
group->type == ControllerEmu::GroupType::MixedTriggers ||
|
group->type == ControllerEmu::GroupType::MixedTriggers ||
|
||||||
group->type == ControllerEmu::GroupType::Force;
|
group->type == ControllerEmu::GroupType::Force ||
|
||||||
|
group->type == ControllerEmu::GroupType::Shake;
|
||||||
|
|
||||||
const bool need_calibration = group->type == ControllerEmu::GroupType::Cursor ||
|
const bool need_calibration = group->type == ControllerEmu::GroupType::Cursor ||
|
||||||
group->type == ControllerEmu::GroupType::Stick ||
|
group->type == ControllerEmu::GroupType::Stick ||
|
||||||
|
@ -129,7 +130,19 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
|
||||||
|
|
||||||
if (need_indicator)
|
if (need_indicator)
|
||||||
{
|
{
|
||||||
auto const indicator = new MappingIndicator(group);
|
MappingIndicator* indicator;
|
||||||
|
|
||||||
|
switch (group->type)
|
||||||
|
{
|
||||||
|
case ControllerEmu::GroupType::Shake:
|
||||||
|
indicator = new ShakeMappingIndicator(static_cast<ControllerEmu::Shake*>(group));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
indicator = new MappingIndicator(group);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
connect(this, &MappingWidget::Update, indicator, QOverload<>::of(&MappingIndicator::update));
|
connect(this, &MappingWidget::Update, indicator, QOverload<>::of(&MappingIndicator::update));
|
||||||
|
|
||||||
if (need_calibration)
|
if (need_calibration)
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -35,7 +37,8 @@ enum class GroupType
|
||||||
Tilt,
|
Tilt,
|
||||||
Cursor,
|
Cursor,
|
||||||
Triggers,
|
Triggers,
|
||||||
Slider
|
Slider,
|
||||||
|
Shake,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ControlGroup
|
class ControlGroup
|
||||||
|
@ -64,6 +67,12 @@ public:
|
||||||
|
|
||||||
void AddDeadzoneSetting(SettingValue<double>* value, double maximum_deadzone);
|
void AddDeadzoneSetting(SettingValue<double>* value, double maximum_deadzone);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T ApplyDeadzone(T input, std::common_type_t<T> deadzone)
|
||||||
|
{
|
||||||
|
return std::copysign(std::max(T{0}, std::abs(input) - deadzone) / (T{1} - deadzone), input);
|
||||||
|
}
|
||||||
|
|
||||||
const std::string name;
|
const std::string name;
|
||||||
const std::string ui_name;
|
const std::string ui_name;
|
||||||
const GroupType type;
|
const GroupType type;
|
||||||
|
|
|
@ -104,8 +104,7 @@ Cursor::StateData Cursor::GetState(const bool adjusted)
|
||||||
const double max_z_step = STEP_Z_PER_SEC / 1000.0 * ms_since_update;
|
const double max_z_step = STEP_Z_PER_SEC / 1000.0 * ms_since_update;
|
||||||
|
|
||||||
// Apply deadzone to z:
|
// Apply deadzone to z:
|
||||||
const ControlState deadzone = GetDeadzonePercentage();
|
z = ApplyDeadzone(z, GetDeadzonePercentage());
|
||||||
z = std::copysign(std::max(0.0, std::abs(z) - deadzone) / (1.0 - deadzone), z);
|
|
||||||
|
|
||||||
// Smooth out z movement:
|
// Smooth out z movement:
|
||||||
// FYI: Not using relative input for Z.
|
// FYI: Not using relative input for Z.
|
||||||
|
|
|
@ -71,8 +71,7 @@ Force::StateData Force::GetState(bool adjusted)
|
||||||
if (adjusted)
|
if (adjusted)
|
||||||
{
|
{
|
||||||
// Apply deadzone to z.
|
// Apply deadzone to z.
|
||||||
const ControlState deadzone = GetDeadzonePercentage();
|
z = ApplyDeadzone(z, GetDeadzonePercentage());
|
||||||
z = std::copysign(std::max(0.0, std::abs(z) - deadzone) / (1.0 - deadzone), z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {float(state.x), float(state.y), float(z)};
|
return {float(state.x), float(state.y), float(z)};
|
||||||
|
@ -105,4 +104,69 @@ ControlState Force::GetDefaultInputRadiusAtAngle(double) const
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shake::Shake(const std::string& name_, ControlState default_intensity_scale)
|
||||||
|
: ControlGroup(name_, name_, GroupType::Shake)
|
||||||
|
{
|
||||||
|
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
||||||
|
controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("X")));
|
||||||
|
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
||||||
|
controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Y")));
|
||||||
|
// i18n: Refers to a 3D axis (used when mapping motion controls)
|
||||||
|
controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z")));
|
||||||
|
|
||||||
|
AddDeadzoneSetting(&m_deadzone_setting, 50);
|
||||||
|
|
||||||
|
// Total travel distance in centimeters.
|
||||||
|
// Negative values can be used to reverse the initial direction of movement.
|
||||||
|
AddSetting(&m_intensity_setting,
|
||||||
|
// i18n: Refers to the intensity of shaking an emulated wiimote.
|
||||||
|
{_trans("Intensity"),
|
||||||
|
// i18n: The symbol/abbreviation for centimeters.
|
||||||
|
_trans("cm"),
|
||||||
|
// i18n: Refering to emulated wii remote movement.
|
||||||
|
_trans("Total travel distance.")},
|
||||||
|
10 * default_intensity_scale, -50, 50);
|
||||||
|
|
||||||
|
// Approximate number of up/down movements in one second.
|
||||||
|
AddSetting(&m_frequency_setting,
|
||||||
|
// i18n: Refers to a number of actions per second in Hz.
|
||||||
|
{_trans("Frequency"),
|
||||||
|
// i18n: The symbol/abbreviation for hertz (cycles per second).
|
||||||
|
_trans("Hz"),
|
||||||
|
// i18n: Refering to emulated wii remote movement.
|
||||||
|
_trans("Number of shakes per second.")},
|
||||||
|
6, 1, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shake::StateData Shake::GetState(bool adjusted) const
|
||||||
|
{
|
||||||
|
const float x = controls[0]->control_ref->State();
|
||||||
|
const float y = controls[1]->control_ref->State();
|
||||||
|
const float z = controls[2]->control_ref->State();
|
||||||
|
|
||||||
|
StateData result = {x, y, z};
|
||||||
|
|
||||||
|
// FYI: Unadjusted values are used in UI.
|
||||||
|
if (adjusted)
|
||||||
|
for (auto& c : result.data)
|
||||||
|
c = ApplyDeadzone(c, GetDeadzone());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlState Shake::GetDeadzone() const
|
||||||
|
{
|
||||||
|
return m_deadzone_setting.GetValue() / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlState Shake::GetIntensity() const
|
||||||
|
{
|
||||||
|
return m_intensity_setting.GetValue() / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlState Shake::GetFrequency() const
|
||||||
|
{
|
||||||
|
return m_frequency_setting.GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ControllerEmu
|
} // namespace ControllerEmu
|
||||||
|
|
|
@ -40,4 +40,28 @@ private:
|
||||||
SettingValue<double> m_jerk_setting;
|
SettingValue<double> m_jerk_setting;
|
||||||
SettingValue<double> m_angle_setting;
|
SettingValue<double> m_angle_setting;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Shake : public ControlGroup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using StateData = Common::Vec3;
|
||||||
|
|
||||||
|
explicit Shake(const std::string& name, ControlState default_intensity_scale = 1);
|
||||||
|
|
||||||
|
StateData GetState(bool adjusted = true) const;
|
||||||
|
|
||||||
|
ControlState GetDeadzone() const;
|
||||||
|
|
||||||
|
// Return total travel distance in meters.
|
||||||
|
ControlState GetIntensity() const;
|
||||||
|
|
||||||
|
// Return frequency in Hz.
|
||||||
|
ControlState GetFrequency() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SettingValue<double> m_deadzone_setting;
|
||||||
|
SettingValue<double> m_intensity_setting;
|
||||||
|
SettingValue<double> m_frequency_setting;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ControllerEmu
|
} // namespace ControllerEmu
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace ControllerEmu
|
||||||
MixedTriggers::MixedTriggers(const std::string& name_)
|
MixedTriggers::MixedTriggers(const std::string& name_)
|
||||||
: ControlGroup(name_, GroupType::MixedTriggers)
|
: ControlGroup(name_, GroupType::MixedTriggers)
|
||||||
{
|
{
|
||||||
|
AddDeadzoneSetting(&m_deadzone_setting, 25);
|
||||||
|
|
||||||
AddSetting(&m_threshold_setting,
|
AddSetting(&m_threshold_setting,
|
||||||
{_trans("Threshold"),
|
{_trans("Threshold"),
|
||||||
// i18n: The percent symbol.
|
// i18n: The percent symbol.
|
||||||
|
@ -27,8 +29,6 @@ MixedTriggers::MixedTriggers(const std::string& name_)
|
||||||
// i18n: Refers to the "threshold" setting for pressure sensitive gamepad inputs.
|
// i18n: Refers to the "threshold" setting for pressure sensitive gamepad inputs.
|
||||||
_trans("Input strength required for activation.")},
|
_trans("Input strength required for activation.")},
|
||||||
90, 0, 100);
|
90, 0, 100);
|
||||||
|
|
||||||
AddDeadzoneSetting(&m_deadzone_setting, 25);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlState* analog,
|
void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlState* analog,
|
||||||
|
@ -46,12 +46,9 @@ void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlSta
|
||||||
const int trigger_count = int(controls.size() / 2);
|
const int trigger_count = int(controls.size() / 2);
|
||||||
for (int i = 0; i != trigger_count; ++i)
|
for (int i = 0; i != trigger_count; ++i)
|
||||||
{
|
{
|
||||||
ControlState button_value = controls[i]->control_ref->State();
|
const ControlState button_value = ApplyDeadzone(controls[i]->control_ref->State(), deadzone);
|
||||||
ControlState analog_value = controls[trigger_count + i]->control_ref->State();
|
ControlState analog_value =
|
||||||
|
ApplyDeadzone(controls[trigger_count + i]->control_ref->State(), deadzone);
|
||||||
// Apply deadzone:
|
|
||||||
analog_value = std::max(0.0, analog_value - deadzone) / (1.0 - deadzone);
|
|
||||||
button_value = std::max(0.0, button_value - deadzone) / (1.0 - deadzone);
|
|
||||||
|
|
||||||
// Apply threshold:
|
// Apply threshold:
|
||||||
if (button_value > threshold)
|
if (button_value > threshold)
|
||||||
|
|
|
@ -34,9 +34,6 @@ Slider::StateData Slider::GetState()
|
||||||
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
|
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
|
||||||
const ControlState state = controls[1]->control_ref->State() - controls[0]->control_ref->State();
|
const ControlState state = controls[1]->control_ref->State() - controls[0]->control_ref->State();
|
||||||
|
|
||||||
if (fabs(state) > deadzone)
|
return {ApplyDeadzone(state, deadzone)};
|
||||||
return {(state - (deadzone * sign(state))) / (1 - deadzone)};
|
|
||||||
|
|
||||||
return {0.0};
|
|
||||||
}
|
}
|
||||||
} // namespace ControllerEmu
|
} // namespace ControllerEmu
|
||||||
|
|
|
@ -28,7 +28,7 @@ Triggers::StateData Triggers::GetState()
|
||||||
|
|
||||||
StateData result(trigger_count);
|
StateData result(trigger_count);
|
||||||
for (size_t i = 0; i < trigger_count; ++i)
|
for (size_t i = 0; i < trigger_count; ++i)
|
||||||
result.data[i] = std::max(controls[i]->control_ref->State() - deadzone, 0.0) / (1 - deadzone);
|
result.data[i] = ApplyDeadzone(controls[i]->control_ref->State(), deadzone);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,8 +272,7 @@ ReshapableInput::ReshapeData ReshapableInput::Reshape(ControlState x, ControlSta
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply deadzone as a percentage of the user-defined calibration shape/size:
|
// Apply deadzone as a percentage of the user-defined calibration shape/size:
|
||||||
const ControlState deadzone = GetDeadzonePercentage();
|
dist = ApplyDeadzone(dist, GetDeadzonePercentage());
|
||||||
dist = std::max(0.0, dist - deadzone) / (1.0 - deadzone);
|
|
||||||
|
|
||||||
// Scale to the gate shape/radius:
|
// Scale to the gate shape/radius:
|
||||||
dist *= gate_max_dist;
|
dist *= gate_max_dist;
|
||||||
|
|
Loading…
Reference in New Issue