This commit is contained in:
Joshua Vandaële 2026-07-01 16:51:08 -04:00 committed by GitHub
commit 4da0610266
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 74 additions and 63 deletions

View File

@ -8,15 +8,18 @@
#include <cmath>
#include <concepts>
#include <limits>
#include <numbers>
#include <type_traits>
#include <utility>
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
namespace MathUtil
{
constexpr double TAU = 6.2831853071795865;
constexpr double PI = TAU / 2;
template <std::floating_point T>
constexpr T TAU_v = std::numbers::pi_v<T> * 2;
constexpr double TAU = TAU_v<double>;
constexpr double GRAVITY_ACCELERATION = 9.80665;
template <typename T>
@ -26,9 +29,12 @@ constexpr auto Sign(const T& val) -> decltype((T{} < val) - (val < T{}))
}
template <typename T, typename F>
constexpr auto Lerp(const T& x, const T& y, const F& a) -> decltype(x + (y - x) * a)
constexpr auto Lerp(const T& x, const T& y, const F& a)
{
return x + (y - x) * a;
if constexpr (std::is_arithmetic_v<T> && std::is_arithmetic_v<F>)
return std::lerp(x, y, a);
else
return x + (y - x) * a;
}
// Casts the specified value to a Dest. The value will be clamped to fit in the destination type.
@ -59,20 +65,18 @@ constexpr Dest SaturatingCast(T value)
template <typename T>
constexpr bool IsPow2(T imm)
{
return imm > 0 && (imm & (imm - 1)) == 0;
if constexpr (std::is_signed_v<T>)
{
if (imm <= 0)
return false;
}
return std::has_single_bit(static_cast<std::make_unsigned_t<T>>(imm));
}
constexpr u32 NextPowerOf2(u32 value)
{
--value;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
++value;
return value;
return std::bit_ceil(value);
}
template <class T>
@ -173,6 +177,20 @@ private:
// Rounds down. 0 -> undefined
constexpr int IntLog2(u64 val)
{
return 63 - std::countl_zero(val);
DEBUG_ASSERT(val != 0);
return std::bit_width(val) - 1;
}
template <std::floating_point T>
constexpr T DegToRad(T degrees) noexcept
{
return degrees * std::numbers::pi_v<T> / T(180);
}
template <std::floating_point T>
constexpr T RadToDeg(T radians) noexcept
{
return radians / std::numbers::pi_v<T> * T(180);
}
} // namespace MathUtil

View File

@ -136,7 +136,7 @@ Vec3 FromQuaternionToEuler(const Quaternion& q)
const float sinp = 2 * (qw * qy - qz * qx);
if (std::abs(sinp) >= 1)
result.y = std::copysign(MathUtil::PI / 2, sinp); // use 90 degrees if out of range
result.y = std::copysign(std::numbers::pi / 2, sinp); // use 90 degrees if out of range
else
result.y = std::asin(sinp);

View File

@ -112,7 +112,7 @@ public:
// Jordan: I calculate the FOV at 42 degrees horizontally and having a 4:3 aspect ratio.
// This is 31.5 degrees vertically.
static constexpr float CAMERA_AR = 4.f / 3;
static constexpr float CAMERA_FOV_X = 42 * float(MathUtil::TAU) / 360;
static constexpr float CAMERA_FOV_X = MathUtil::DegToRad(42.0f);
static constexpr float CAMERA_FOV_Y = CAMERA_FOV_X / CAMERA_AR;
enum : u8

View File

@ -112,8 +112,8 @@ void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* const tilt_group,
const auto target = tilt_group->GetState();
// 180 degrees is currently the max tilt value.
const ControlState roll = target.x * MathUtil::PI;
const ControlState pitch = target.y * MathUtil::PI;
const ControlState roll = target.x * std::numbers::pi;
const ControlState pitch = target.y * std::numbers::pi;
const auto target_angle = Common::Vec3(pitch, -roll, 0);
@ -121,7 +121,7 @@ void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* const tilt_group,
for (std::size_t i = 0; i != target_angle.data.size(); ++i)
{
auto& angle = state->angle.data[i];
if (std::abs(angle - target_angle.data[i]) > float(MathUtil::PI))
if (std::abs(angle - target_angle.data[i]) > std::numbers::pi_v<float>)
angle -= std::copysign(MathUtil::TAU, angle);
}
@ -265,7 +265,7 @@ void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
// Higher values will be more responsive but increase rate of M+ "desync".
// I'd rather not expose this value in the UI if not needed.
// At this value, sync is very good and responsiveness still appears instant.
constexpr auto MAX_ACCEL = float(MathUtil::TAU * 8);
constexpr auto MAX_ACCEL = MathUtil::TAU_v<float> * 8;
ApproachAngleWithAccel(state, target_angle, MAX_ACCEL, time_elapsed);
}

View File

@ -62,7 +62,7 @@ Common::Vec3 MotionPlus::DataFormat::Data::GetAngularVelocity(const CalibrationB
const auto sign_fix = Common::Vec3(-1, +1, -1);
// Adjust deg/s to rad/s.
constexpr auto scalar = float(MathUtil::TAU / 360);
constexpr auto scalar = MathUtil::DegToRad(1.0f);
return gyro.GetNormalizedValue(calibration.value) * sign_fix * Common::Vec3(calibration.degrees) *
scalar;

View File

@ -131,7 +131,7 @@ public:
static constexpr u16 VALUE_SCALE =
(CALIBRATION_SCALE_OFFSET >> (CALIBRATION_BITS - BITS_OF_PRECISION));
static constexpr float VALUE_SCALE_DEGREES = VALUE_SCALE / float(MathUtil::TAU) * 360;
static constexpr float VALUE_SCALE_DEGREES = MathUtil::RadToDeg(static_cast<float>(VALUE_SCALE));
static constexpr float SLOW_SCALE = VALUE_SCALE_DEGREES / CALIBRATION_SLOW_SCALE_DEGREES;
static constexpr float FAST_SCALE = VALUE_SCALE_DEGREES / CALIBRATION_FAST_SCALE_DEGREES;

View File

@ -231,8 +231,8 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i
groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing")));
groups.emplace_back(m_imu_ir = new ControllerEmu::IMUCursor("IMUIR", _trans("Point")));
const auto fov_default =
Common::DVec2(CameraLogic::CAMERA_FOV_X, CameraLogic::CAMERA_FOV_Y) / MathUtil::TAU * 360;
const auto fov_default = Common::DVec2(MathUtil::RadToDeg(CameraLogic::CAMERA_FOV_X),
MathUtil::RadToDeg(CameraLogic::CAMERA_FOV_Y));
m_imu_ir->AddSetting(&m_fov_x_setting,
// i18n: FOV stands for "Field of view".
{_trans("Horizontal FOV"),
@ -508,9 +508,8 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
else if (sensor_bar_state == SensorBarState::Enabled)
{
target_state->camera_points = CameraLogic::GetCameraPoints(
GetTotalTransformation(),
Common::Vec2(m_fov_x_setting.GetValue(), m_fov_y_setting.GetValue()) / 360 *
float(MathUtil::TAU));
GetTotalTransformation(), Common::Vec2(MathUtil::DegToRad(m_fov_x_setting.GetValue()),
MathUtil::DegToRad(m_fov_y_setting.GetValue())));
}
else
{

View File

@ -275,7 +275,7 @@ void DrawVirtualNotches(QPainter& p, ControllerEmu::ReshapableInput& stick, QCol
template <typename F>
void GenerateFibonacciSphere(int point_count, F&& callback)
{
const float golden_angle = MathUtil::PI * (3.f - std::sqrt(5.f));
const float golden_angle = std::numbers::pi * (3.f - std::sqrt(5.f));
for (int i = 0; i != point_count; ++i)
{
@ -462,7 +462,8 @@ QColor TiltIndicator::GetGateBrushColor() const
void TiltIndicator::Draw()
{
auto adj_coord = Common::DVec2{-m_motion_state.angle.y, m_motion_state.angle.x} / MathUtil::PI;
auto adj_coord =
Common::DVec2{-m_motion_state.angle.y, m_motion_state.angle.x} / std::numbers::pi;
// Angle values after dividing by pi.
constexpr auto norm_180_deg = 1;

View File

@ -99,7 +99,7 @@ OctagonAnalogStick::OctagonAnalogStick(const char* name_, const char* ui_name_,
ControlState OctagonAnalogStick::GetVirtualNotchSize() const
{
return m_virtual_notch_setting.GetValue() * MathUtil::TAU / 360;
return MathUtil::DegToRad(m_virtual_notch_setting.GetValue());
}
ControlState OctagonAnalogStick::GetGateRadiusAtAngle(double ang) const

View File

@ -167,12 +167,12 @@ Cursor::StateData Cursor::UpdateState(Cursor::ReshapeData input)
ControlState Cursor::GetTotalYaw() const
{
return m_yaw_setting.GetValue() * MathUtil::TAU / 360;
return MathUtil::DegToRad(m_yaw_setting.GetValue());
}
ControlState Cursor::GetTotalPitch() const
{
return m_pitch_setting.GetValue() * MathUtil::TAU / 360;
return MathUtil::DegToRad(m_pitch_setting.GetValue());
}
ControlState Cursor::GetVerticalOffset() const

View File

@ -106,7 +106,7 @@ ControlState Force::GetReturnSpeed() const
ControlState Force::GetTwistAngle() const
{
return m_angle_setting.GetValue() * MathUtil::TAU / 360;
return MathUtil::DegToRad(m_angle_setting.GetValue());
}
ControlState Force::GetMaxDistance() const

View File

@ -49,7 +49,7 @@ IMUCursor::IMUCursor(std::string name_, std::string ui_name_)
ControlState IMUCursor::GetTotalYaw() const
{
return m_yaw_setting.GetValue() * MathUtil::TAU / 360;
return MathUtil::DegToRad(m_yaw_setting.GetValue());
}
ControlState IMUCursor::GetAccelWeight() const

View File

@ -163,7 +163,7 @@ std::optional<IMUGyroscope::StateData> IMUGyroscope::GetState(bool update)
ControlState IMUGyroscope::GetDeadzone() const
{
return m_deadzone_setting.GetValue() / 360 * MathUtil::TAU;
return MathUtil::DegToRad(m_deadzone_setting.GetValue());
}
bool IMUGyroscope::IsCalibrating() const

View File

@ -69,7 +69,7 @@ ControlState Tilt::GetDefaultInputRadiusAtAngle(double ang) const
ControlState Tilt::GetMaxRotationalVelocity() const
{
return m_max_rotational_velocity.GetValue() * MathUtil::TAU;
return m_max_rotational_velocity.GetValue() * MathUtil::TAU_v<ControlState>;
}
Control* Tilt::GetModifierInput() const

View File

@ -54,7 +54,7 @@ double GetNearestNotch(double angle, double virtual_notch_angle)
constexpr auto rounding = MathUtil::TAU / sides;
const auto closest_notch = std::round(angle / rounding) * rounding;
const auto angle_diff =
std::fmod(angle - closest_notch + MathUtil::PI, MathUtil::TAU) - MathUtil::PI;
std::fmod(angle - closest_notch + std::numbers::pi, MathUtil::TAU) - std::numbers::pi;
return std::abs(angle_diff) < virtual_notch_angle / 2 ? closest_notch : angle;
}
@ -80,12 +80,12 @@ OctagonStickGate::OctagonStickGate(ControlState radius) : m_radius(radius)
ControlState OctagonStickGate::GetRadiusAtAngle(double angle) const
{
constexpr int sides = 8;
constexpr double sum_int_angles = (sides - 2) * MathUtil::PI;
constexpr double sum_int_angles = (sides - 2) * std::numbers::pi;
constexpr double half_int_angle = sum_int_angles / sides / 2;
angle = std::fmod(angle, MathUtil::TAU / sides);
// Solve ASA triangle using The Law of Sines:
return m_radius / std::sin(MathUtil::PI - angle - half_int_angle) * std::sin(half_int_angle);
return m_radius / std::sin(std::numbers::pi - angle - half_int_angle) * std::sin(half_int_angle);
}
std::optional<u32> OctagonStickGate::GetIdealCalibrationSampleCount() const
@ -306,7 +306,7 @@ ReshapableInput::ReshapeData ReshapableInput::Reshape(ControlState x, ControlSta
// (which depends on the signs of x and y) does not matter here as dist is zero
// TODO: make the AtAngle functions work with negative angles:
ControlState angle = std::atan2(y, x) + MathUtil::TAU;
ControlState angle = std::atan2(y, x) + MathUtil::TAU_v<ControlState>;
const ControlState input_max_dist = GetInputRadiusAtAngle(angle);
ControlState gate_max_dist = GetGateRadiusAtAngle(angle);

View File

@ -574,7 +574,7 @@ Device::Device(std::string name, int index, std::string server_address, u16 serv
AddInput(new AccelerometerInput("Accel Backward", m_pad_data.accelerometer_z_g, -accel_scale));
// Convert degrees per second to radians per second
constexpr auto gyro_scale = 360.0 / MathUtil::TAU;
constexpr auto gyro_scale = MathUtil::RadToDeg(1.0);
AddInput(new GyroInput("Gyro Pitch Up", m_pad_data.gyro_pitch_deg_s, gyro_scale));
AddInput(new GyroInput("Gyro Pitch Down", m_pad_data.gyro_pitch_deg_s, -gyro_scale));

View File

@ -189,7 +189,7 @@ bool ReshapableInputMapper::Update()
float ReshapableInputMapper::GetCurrentAngle() const
{
constexpr auto quarter_circle = float(MathUtil::TAU) * 0.25f;
constexpr auto quarter_circle = MathUtil::TAU_v<float> * 0.25f;
return quarter_circle - (float(m_input_detector.GetResults().size()) * quarter_circle);
}

View File

@ -257,7 +257,7 @@ Device::Device(hid_device* device) : m_device{device}
// 16.384 (?) LSBs = 1 deg / s
// final output in rads / s
constexpr auto gyro_scale = 16.384 * 360.0 / MathUtil::TAU;
constexpr auto gyro_scale = MathUtil::RadToDeg(16.384);
AddInput(new MotionInput("Gyro Pitch Up", m_latest_input.gyro_pitch, gyro_scale));
AddInput(new MotionInput("Gyro Pitch Down", m_latest_input.gyro_pitch, -gyro_scale));
AddInput(new MotionInput("Gyro Roll Left", m_latest_input.gyro_roll, -gyro_scale));

View File

@ -1258,7 +1258,7 @@ void Device::UpdateOrientation()
m_rotation_inputs =
Common::Vec3{WiimoteEmu::GetPitch(m_orientation), WiimoteEmu::GetRoll(m_orientation),
WiimoteEmu::GetYaw(m_orientation)} /
float(MathUtil::PI);
std::numbers::pi_v<float>;
}
void Device::IRState::ProcessData(const DataReportManipulator& manipulator)

View File

@ -538,7 +538,7 @@ bool evdevDevice::AddNode(std::string devnode, int fd, libevdev* dev)
// evdev resolution is specified in "g"s and deg/s.
// Convert these to m/s/s and rad/s.
constexpr ControlState accel_scale = MathUtil::GRAVITY_ACCELERATION;
constexpr ControlState gyro_scale = MathUtil::TAU / 360;
constexpr ControlState gyro_scale = MathUtil::DegToRad(ControlState{1});
add_motion_inputs(ABS_X, accel_scale);
add_motion_inputs(ABS_RX, gyro_scale);

View File

@ -7,6 +7,7 @@
#include <cstddef>
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/Matrix.h"
struct InputVertexData
@ -40,35 +41,27 @@ struct OutputVertexData
void Lerp(float t, const OutputVertexData* a, const OutputVertexData* b)
{
#define LINTERP(T, OUT, IN) (OUT) + ((IN - OUT) * T)
mvPosition = MathUtil::Lerp(a->mvPosition, b->mvPosition, t);
#define LINTERP_INT(T, OUT, IN) (OUT) + (((IN - OUT) * T) >> 8)
mvPosition = LINTERP(t, a->mvPosition, b->mvPosition);
projectedPosition.x = LINTERP(t, a->projectedPosition.x, b->projectedPosition.x);
projectedPosition.y = LINTERP(t, a->projectedPosition.y, b->projectedPosition.y);
projectedPosition.z = LINTERP(t, a->projectedPosition.z, b->projectedPosition.z);
projectedPosition.w = LINTERP(t, a->projectedPosition.w, b->projectedPosition.w);
projectedPosition.x = MathUtil::Lerp(a->projectedPosition.x, b->projectedPosition.x, t);
projectedPosition.y = MathUtil::Lerp(a->projectedPosition.y, b->projectedPosition.y, t);
projectedPosition.z = MathUtil::Lerp(a->projectedPosition.z, b->projectedPosition.z, t);
projectedPosition.w = MathUtil::Lerp(a->projectedPosition.w, b->projectedPosition.w, t);
for (std::size_t i = 0; i < normal.size(); ++i)
{
normal[i] = LINTERP(t, a->normal[i], b->normal[i]);
normal[i] = MathUtil::Lerp(a->normal[i], b->normal[i], t);
}
const u16 t_int = static_cast<u16>(t * 256);
for (std::size_t i = 0; i < color[0].size(); ++i)
{
color[0][i] = LINTERP_INT(t_int, a->color[0][i], b->color[0][i]);
color[1][i] = LINTERP_INT(t_int, a->color[1][i], b->color[1][i]);
color[0][i] = static_cast<u8>(MathUtil::Lerp(a->color[0][i], b->color[0][i], t));
color[1][i] = static_cast<u8>(MathUtil::Lerp(a->color[1][i], b->color[1][i], t));
}
for (std::size_t i = 0; i < texCoords.size(); ++i)
{
texCoords[i] = LINTERP(t, a->texCoords[i], b->texCoords[i]);
texCoords[i] = MathUtil::Lerp(a->texCoords[i], b->texCoords[i], t);
}
#undef LINTERP
#undef LINTERP_INT
}
};