ControllerEmu: Add new "input override" system

This commit is contained in:
JosJuice
2021-03-21 20:27:00 +01:00
parent cb6d476538
commit cb16d20f2d
27 changed files with 355 additions and 74 deletions

View File

@ -138,14 +138,15 @@ GCPadStatus GCPad::GetInput() const
const auto lock = GetStateLock();
GCPadStatus pad = {};
if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected()))
if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected() ||
m_input_override_function))
{
pad.isConnected = false;
return pad;
}
// buttons
m_buttons->GetState(&pad.button, button_bitmasks);
m_buttons->GetState(&pad.button, button_bitmasks, m_input_override_function);
// set analog A/B analog to full or w/e, prolly not needed
if (pad.button & PAD_BUTTON_A)
@ -154,20 +155,20 @@ GCPadStatus GCPad::GetInput() const
pad.analogB = 0xFF;
// dpad
m_dpad->GetState(&pad.button, dpad_bitmasks);
m_dpad->GetState(&pad.button, dpad_bitmasks, m_input_override_function);
// sticks
const auto main_stick_state = m_main_stick->GetState();
const auto main_stick_state = m_main_stick->GetState(m_input_override_function);
pad.stickX = MapFloat<u8>(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X, 1);
pad.stickY = MapFloat<u8>(main_stick_state.y, GCPadStatus::MAIN_STICK_CENTER_Y, 1);
const auto c_stick_state = m_c_stick->GetState();
const auto c_stick_state = m_c_stick->GetState(m_input_override_function);
pad.substickX = MapFloat<u8>(c_stick_state.x, GCPadStatus::C_STICK_CENTER_X, 1);
pad.substickY = MapFloat<u8>(c_stick_state.y, GCPadStatus::C_STICK_CENTER_Y, 1);
// triggers
std::array<ControlState, 2> triggers;
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data());
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data(), m_input_override_function);
pad.triggerLeft = MapFloat<u8>(triggers[0], 0);
pad.triggerRight = MapFloat<u8>(triggers[1], 0);

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include <cmath>
#include <optional>
#include "Common/MathUtil.h"
#include "Core/Config/SYSCONFSettings.h"
@ -221,9 +222,10 @@ WiimoteCommon::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g,
u16(std::clamp(std::lround(scaled_accel.z + zero_g), 0l, MAX_VALUE))});
}
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed)
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
const ControllerEmu::InputOverrideFunction& override_func, float time_elapsed)
{
const auto cursor = ir_group->GetState(true);
const auto cursor = ir_group->GetState(true, override_func);
if (!cursor.IsVisible())
{

View File

@ -15,6 +15,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
namespace WiimoteEmu
{
@ -81,7 +82,8 @@ void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& target,
void EmulateShake(PositionalState* state, ControllerEmu::Shake* shake_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 EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed);
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
const ControllerEmu::InputOverrideFunction& override_func, float time_elapsed);
void EmulateIMUCursor(IMUCursorState* state, ControllerEmu::IMUCursor* imu_ir_group,
ControllerEmu::IMUAccelerometer* imu_accelerometer_group,
ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed);

View File

@ -113,7 +113,8 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// left stick
{
const ControllerEmu::AnalogStick::StateData left_stick_state = m_left_stick->GetState();
const ControllerEmu::AnalogStick::StateData left_stick_state =
m_left_stick->GetState(m_input_override_function);
const u8 x = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.x * LEFT_STICK_RADIUS));
const u8 y = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.y * LEFT_STICK_RADIUS));
@ -123,7 +124,8 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// right stick
{
const ControllerEmu::AnalogStick::StateData right_stick_data = m_right_stick->GetState();
const ControllerEmu::AnalogStick::StateData right_stick_data =
m_right_stick->GetState(m_input_override_function);
const u8 x = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.x * RIGHT_STICK_RADIUS));
const u8 y = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.y * RIGHT_STICK_RADIUS));
@ -135,19 +137,20 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// triggers
{
ControlState trigs[2] = {0, 0};
m_triggers->GetState(&buttons, classic_trigger_bitmasks.data(), trigs);
ControlState triggers[2] = {0, 0};
m_triggers->GetState(&buttons, classic_trigger_bitmasks.data(), triggers,
m_input_override_function);
const u8 lt = static_cast<u8>(trigs[0] * TRIGGER_RANGE);
const u8 rt = static_cast<u8>(trigs[1] * TRIGGER_RANGE);
const u8 lt = static_cast<u8>(triggers[0] * TRIGGER_RANGE);
const u8 rt = static_cast<u8>(triggers[1] * TRIGGER_RANGE);
classic_data.SetLeftTrigger(lt);
classic_data.SetRightTrigger(rt);
}
// buttons and dpad
m_buttons->GetState(&buttons, classic_button_bitmasks.data());
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data());
m_buttons->GetState(&buttons, classic_button_bitmasks.data(), m_input_override_function);
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data(), m_input_override_function);
classic_data.SetButtons(buttons);

View File

@ -47,7 +47,7 @@ void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_st
constexpr double CENTER_X = (MAX_X + MIN_X) / 2.0;
constexpr double CENTER_Y = (MAX_Y + MIN_Y) / 2.0;
const auto stylus_state = m_stylus->GetState();
const auto stylus_state = m_stylus->GetState(m_input_override_function);
const auto stylus_x = u16(std::lround(CENTER_X + stylus_state.x * (MAX_X - CENTER_X)));
const auto stylus_y = u16(std::lround(CENTER_Y + stylus_state.y * (MAX_Y - CENTER_Y)));
@ -74,7 +74,7 @@ void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_st
// Pressure (0 - 0x7ff):
constexpr u16 MAX_PRESSURE = 0x7ff;
const auto touch_state = m_touch->GetState();
const auto touch_state = m_touch->GetState(m_input_override_function);
const auto pressure = u16(std::lround(touch_state.data[0] * MAX_PRESSURE));
tablet_data.pressure1 = u8(pressure);

View File

@ -84,17 +84,18 @@ void Drums::BuildDesiredExtensionState(DesiredExtensionState* target_state)
DesiredState& state = target_state->data.emplace<DesiredState>();
{
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
state.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
state.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
}
state.buttons = 0;
m_buttons->GetState(&state.buttons, drum_button_bitmasks.data());
m_buttons->GetState(&state.buttons, drum_button_bitmasks.data(), m_input_override_function);
state.drum_pads = 0;
m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data());
m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data(), m_input_override_function);
state.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100));
}

View File

@ -101,7 +101,8 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// stick
{
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
guitar_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER);
guitar_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER);
@ -111,7 +112,8 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
if (m_slider_bar->controls[0]->control_ref->BoundCount() &&
m_slider_bar->controls[1]->control_ref->BoundCount())
{
const ControllerEmu::Slider::StateData slider_data = m_slider_bar->GetState();
const ControllerEmu::Slider::StateData slider_data =
m_slider_bar->GetState(m_input_override_function);
guitar_data.sb = s_slider_bar_control_codes.lower_bound(slider_data.value)->second;
}
@ -122,17 +124,18 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
}
// whammy bar
const ControllerEmu::Triggers::StateData whammy_state = m_whammy->GetState();
const ControllerEmu::Triggers::StateData whammy_state =
m_whammy->GetState(m_input_override_function);
guitar_data.whammy = static_cast<u8>(whammy_state.data[0] * 0x1F);
// buttons
m_buttons->GetState(&guitar_data.bt, guitar_button_bitmasks.data());
m_buttons->GetState(&guitar_data.bt, guitar_button_bitmasks.data(), m_input_override_function);
// frets
m_frets->GetState(&guitar_data.bt, guitar_fret_bitmasks.data());
m_frets->GetState(&guitar_data.bt, guitar_fret_bitmasks.data(), m_input_override_function);
// strum
m_strum->GetState(&guitar_data.bt, guitar_strum_bitmasks.data());
m_strum->GetState(&guitar_data.bt, guitar_strum_bitmasks.data(), m_input_override_function);
// flip button bits
guitar_data.bt ^= 0xFFFF;

View File

@ -67,29 +67,34 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
DataFormat nc_data = {};
// stick
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
bool override_occurred = false;
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function, &override_occurred);
nc_data.jx = u8(STICK_CENTER + stick_state.x * STICK_RADIUS);
nc_data.jy = u8(STICK_CENTER + stick_state.y * STICK_RADIUS);
// Some terribly coded games check whether to move with a check like
//
// if (x != 0 && y != 0)
// do_movement(x, y);
//
// With keyboard controls, these games break if you simply hit
// of the axes. Adjust this if you're hitting one of the axes so that
// we slightly tweak the other axis.
if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER)
if (!override_occurred)
{
if (nc_data.jx == STICK_CENTER)
++nc_data.jx;
if (nc_data.jy == STICK_CENTER)
++nc_data.jy;
// Some terribly coded games check whether to move with a check like
//
// if (x != 0 && y != 0)
// do_movement(x, y);
//
// With keyboard controls, these games break if you simply hit one
// of the axes. Adjust this if you're hitting one of the axes so that
// we slightly tweak the other axis.
if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER)
{
if (nc_data.jx == STICK_CENTER)
++nc_data.jx;
if (nc_data.jy == STICK_CENTER)
++nc_data.jy;
}
}
// buttons
u8 buttons = 0;
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data());
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data(), m_input_override_function);
nc_data.SetButtons(buttons);
// Acceleration data:
@ -108,6 +113,8 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// shake
accel += m_shake_state.acceleration;
accel = Wiimote::OverrideVec3(m_imu_accelerometer, accel, m_input_override_function);
// 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);
nc_data.SetAccel(acc.value);

View File

@ -54,8 +54,8 @@ void TaTaCon::BuildDesiredExtensionState(DesiredExtensionState* target_state)
{
DataFormat tatacon_data = {};
m_center->GetState(&tatacon_data.state, center_bitmasks.data());
m_rim->GetState(&tatacon_data.state, rim_bitmasks.data());
m_center->GetState(&tatacon_data.state, center_bitmasks.data(), m_input_override_function);
m_rim->GetState(&tatacon_data.state, rim_bitmasks.data(), m_input_override_function);
// Flip button bits.
tatacon_data.state ^= 0xff;

View File

@ -86,7 +86,8 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// stick
{
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
tt_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER);
tt_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER);
@ -94,7 +95,7 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// left table
{
const ControllerEmu::Slider::StateData lt = m_left_table->GetState();
const ControllerEmu::Slider::StateData lt = m_left_table->GetState(m_input_override_function);
const s8 tt = static_cast<s8>(lt.value * TABLE_RANGE);
tt_data.ltable1 = tt;
@ -103,7 +104,7 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// right table
{
const ControllerEmu::Slider::StateData rt = m_right_table->GetState();
const ControllerEmu::Slider::StateData rt = m_right_table->GetState(m_input_override_function);
const s8 tt = static_cast<s8>(rt.value * TABLE_RANGE);
tt_data.rtable1 = tt;
@ -114,7 +115,7 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// effect dial
{
const auto dial_state = m_effect_dial->GetState();
const auto dial_state = m_effect_dial->GetState(m_input_override_function);
const u8 dial = static_cast<u8>(dial_state.value * EFFECT_DIAL_RANGE) + EFFECT_DIAL_CENTER;
tt_data.dial1 = dial;
@ -123,13 +124,13 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// crossfade slider
{
const ControllerEmu::Slider::StateData cfs = m_crossfade->GetState();
const ControllerEmu::Slider::StateData cfs = m_crossfade->GetState(m_input_override_function);
tt_data.slider = static_cast<u8>((cfs.value * CROSSFADE_RANGE) + CROSSFADE_CENTER);
}
// buttons
m_buttons->GetState(&tt_data.bt, turntable_button_bitmasks.data());
m_buttons->GetState(&tt_data.bt, turntable_button_bitmasks.data(), m_input_override_function);
// flip button bits :/
tt_data.bt ^= (BUTTON_L_GREEN | BUTTON_L_RED | BUTTON_L_BLUE | BUTTON_R_GREEN | BUTTON_R_RED |

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include <memory>
#include <optional>
#include <string_view>
#include <fmt/format.h>
@ -458,9 +459,10 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
// Fetch pressed buttons from user input.
target_state->buttons.hex = 0;
m_buttons->GetState(&target_state->buttons.hex, button_bitmasks);
m_buttons->GetState(&target_state->buttons.hex, button_bitmasks, m_input_override_function);
m_dpad->GetState(&target_state->buttons.hex,
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks,
m_input_override_function);
// Calculate accelerometer state.
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
@ -628,9 +630,6 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
std::fill_n(ext_data, ext_size, u8(0xff));
}
}
Movie::CallWiiInputManip(rpt_builder, m_bt_device_index, m_active_extension,
GetExtensionEncryptionKey());
}
Movie::CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension,
@ -651,8 +650,9 @@ ButtonData Wiimote::GetCurrentlyPressedButtons()
const auto lock = GetStateLock();
ButtonData buttons{};
m_buttons->GetState(&buttons.hex, button_bitmasks);
m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
m_buttons->GetState(&buttons.hex, button_bitmasks, m_input_override_function);
m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks,
m_input_override_function);
return buttons;
}
@ -789,7 +789,7 @@ void Wiimote::StepDynamics()
{
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
EmulatePoint(&m_point_state, m_ir, 1.f / ::Wiimote::UPDATE_FREQ);
EmulatePoint(&m_point_state, m_ir, m_input_override_function, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateIMUCursor(&m_imu_cursor_state, m_imu_ir, m_imu_accelerometer, m_imu_gyroscope,
1.f / ::Wiimote::UPDATE_FREQ);
@ -831,20 +831,87 @@ Common::Quaternion Wiimote::GetOrientation() const
Common::Quaternion::RotateX(float(MathUtil::TAU / 4 * IsUpright()));
}
std::optional<Common::Vec3> Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group,
std::optional<Common::Vec3> optional_vec) const
{
bool has_value = optional_vec.has_value();
Common::Vec3 vec = has_value ? *optional_vec : Common::Vec3{};
if (m_input_override_function)
{
if (const std::optional<ControlState> x_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE, vec.x))
{
has_value = true;
vec.x = *x_override;
}
if (const std::optional<ControlState> y_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE, vec.y))
{
has_value = true;
vec.y = *y_override;
}
if (const std::optional<ControlState> z_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE, vec.z))
{
has_value = true;
vec.z = *z_override;
}
}
return has_value ? std::make_optional(vec) : std::nullopt;
}
Common::Vec3 Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group,
Common::Vec3 vec) const
{
return OverrideVec3(control_group, vec, m_input_override_function);
}
Common::Vec3
Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
const ControllerEmu::InputOverrideFunction& input_override_function)
{
if (input_override_function)
{
if (const std::optional<ControlState> x_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE, vec.x))
{
vec.x = *x_override;
}
if (const std::optional<ControlState> y_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE, vec.y))
{
vec.y = *y_override;
}
if (const std::optional<ControlState> z_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE, vec.z))
{
vec.z = *z_override;
}
}
return vec;
}
Common::Vec3 Wiimote::GetTotalAcceleration() const
{
if (const auto accel = m_imu_accelerometer->GetState())
return GetAcceleration(*accel);
const Common::Vec3 default_accel = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION));
const Common::Vec3 accel = m_imu_accelerometer->GetState().value_or(default_accel);
return GetAcceleration();
return OverrideVec3(m_imu_accelerometer, GetAcceleration(accel));
}
Common::Vec3 Wiimote::GetTotalAngularVelocity() const
{
if (const auto ang_vel = m_imu_gyroscope->GetState())
return GetAngularVelocity(*ang_vel);
const Common::Vec3 default_ang_vel = {};
const Common::Vec3 ang_vel = m_imu_gyroscope->GetState().value_or(default_ang_vel);
return GetAngularVelocity();
return OverrideVec3(m_imu_gyroscope, GetAngularVelocity(ang_vel));
}
Common::Matrix44 Wiimote::GetTotalTransformation() const

View File

@ -5,6 +5,7 @@
#include <array>
#include <numeric>
#include <optional>
#include <string>
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
@ -146,6 +147,10 @@ public:
// Active extension number is exposed for TAS.
ExtensionNumber GetActiveExtensionNumber() const;
static Common::Vec3
OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
const ControllerEmu::InputOverrideFunction& input_override_function);
private:
// Used only for error generation:
static constexpr u8 EEPROM_I2C_ADDR = 0x50;
@ -161,11 +166,10 @@ private:
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state);
// Returns simulated accelerometer data in m/s^2.
Common::Vec3 GetAcceleration(
Common::Vec3 extra_acceleration = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION))) const;
Common::Vec3 GetAcceleration(Common::Vec3 extra_acceleration) const;
// Returns simulated gyroscope data in radians/s.
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity = {}) const;
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity) const;
// Returns the transformation of the world around the wiimote.
// Used for simulating camera data and for rotating acceleration data.
@ -176,6 +180,10 @@ private:
// Returns the world rotation from the effects of sideways/upright settings.
Common::Quaternion GetOrientation() const;
std::optional<Common::Vec3> OverrideVec3(const ControllerEmu::ControlGroup* control_group,
std::optional<Common::Vec3> optional_vec) const;
Common::Vec3 OverrideVec3(const ControllerEmu::ControlGroup* control_group,
Common::Vec3 vec) const;
Common::Vec3 GetTotalAcceleration() const;
Common::Vec3 GetTotalAngularVelocity() const;
Common::Matrix44 GetTotalTransformation() const;