1
0
Fork 0

Add support for joypad motion sensors

This commit is contained in:
Nintorch 2025-12-05 17:47:31 +05:00
parent 481f36ed20
commit a3eb202b25
10 changed files with 1940 additions and 0 deletions

View File

@ -310,6 +310,11 @@ Comment: The FreeType Project
Copyright: 1996-2025, David Turner, Robert Wilhelm, and Werner Lemberg.
License: FTL
Files: thirdparty/gamepadmotionhelpers/*
Comment: GamepadMotionHelpers
Copyright: 2020-2023, Julian "Jibb" Smart
License: Expat
Files: thirdparty/glad/*
Comment: glad
Copyright: 2013-2022, David Herberth

View File

@ -40,6 +40,10 @@
#include "core/os/thread.h"
#endif
#include "thirdparty/gamepadmotionhelpers/GamepadMotion.hpp"
#define STANDARD_GRAVITY 9.80665f
static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {
"a",
"b",
@ -147,6 +151,20 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);
ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope);
ClassDB::bind_method(D_METHOD("get_joy_accelerometer", "device"), &Input::get_joy_accelerometer);
ClassDB::bind_method(D_METHOD("get_joy_gravity", "device"), &Input::get_joy_gravity);
ClassDB::bind_method(D_METHOD("get_joy_gyroscope", "device"), &Input::get_joy_gyroscope);
ClassDB::bind_method(D_METHOD("get_joy_motion_sensors_rate", "device"), &Input::get_joy_motion_sensors_rate);
ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_enabled", "device"), &Input::is_joy_motion_sensors_enabled);
ClassDB::bind_method(D_METHOD("set_joy_motion_sensors_enabled", "device", "enable"), &Input::set_joy_motion_sensors_enabled);
ClassDB::bind_method(D_METHOD("has_joy_motion_sensors", "device"), &Input::has_joy_motion_sensors);
ClassDB::bind_method(D_METHOD("start_joy_motion_sensors_calibration", "device"), &Input::start_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("stop_joy_motion_sensors_calibration", "device"), &Input::stop_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("clear_joy_motion_sensors_calibration", "device"), &Input::clear_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("get_joy_motion_sensors_calibration", "device"), &Input::get_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("set_joy_motion_sensors_calibration", "device", "calibration_info"), &Input::set_joy_motion_sensors_calibration);
ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_calibrated", "device"), &Input::is_joy_motion_sensors_calibrated);
ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_calibrating", "device"), &Input::is_joy_motion_sensors_calibrating);
ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity);
ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer);
ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer);
@ -684,6 +702,11 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
for (int i = 0; i < (int)JoyAxis::MAX; i++) {
set_joy_axis(p_idx, (JoyAxis)i, 0.0f);
}
MotionInfo *motion = joy_motion.getptr(p_idx);
if (motion != nullptr && motion->gamepad_motion != nullptr) {
delete motion->gamepad_motion;
}
joy_motion.erase(p_idx);
}
joy_names[p_idx] = js;
@ -1018,6 +1041,196 @@ bool Input::has_joy_light(int p_device) const {
return joypad && joypad->has_light;
}
Vector3 Input::get_joy_accelerometer(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Vector3();
}
float joy_acceleration_data[3];
motion->gamepad_motion->GetProcessedAcceleration(joy_acceleration_data[0], joy_acceleration_data[1], joy_acceleration_data[2]);
Vector3 joy_acceleration(joy_acceleration_data[0], joy_acceleration_data[1], joy_acceleration_data[2]);
float joy_gravity_data[3];
motion->gamepad_motion->GetGravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
Vector3 joy_gravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
return (-joy_acceleration + joy_gravity) * STANDARD_GRAVITY;
}
Vector3 Input::get_joy_gravity(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Vector3();
}
float joy_gravity_data[3];
motion->gamepad_motion->GetGravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
Vector3 joy_gravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]);
return joy_gravity.normalized() * STANDARD_GRAVITY;
}
Vector3 Input::get_joy_gyroscope(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Vector3();
}
float joy_gyro_data[3];
motion->gamepad_motion->GetCalibratedGyro(joy_gyro_data[0], joy_gyro_data[1], joy_gyro_data[2]);
Vector3 joy_gyro(joy_gyro_data[0], joy_gyro_data[1], joy_gyro_data[2]);
return joy_gyro * M_PI / 180.0;
}
void Input::set_joy_motion_sensors_enabled(int p_device, bool p_enable) {
_THREAD_SAFE_METHOD_
Joypad *joypad = joy_names.getptr(p_device);
if (joypad == nullptr || joypad->features == nullptr) {
return;
}
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
joypad->features->set_joy_motion_sensors_enabled(p_enable);
motion->sensors_enabled = p_enable;
}
bool Input::is_joy_motion_sensors_enabled(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
return motion != nullptr && motion->sensors_enabled;
}
bool Input::has_joy_motion_sensors(int p_device) const {
_THREAD_SAFE_METHOD_
return joy_motion.has(p_device);
}
float Input::get_joy_motion_sensors_rate(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return 0.0f;
}
return motion->sensor_data_rate;
}
void Input::start_joy_motion_sensors_calibration(int p_device) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
ERR_FAIL_COND_MSG(!motion->sensors_enabled, "Motion sensors are not enabled on the joypad.");
ERR_FAIL_COND_MSG(motion->calibrating, "Calibration already in progress.");
motion->gamepad_motion->ResetContinuousCalibration();
motion->gamepad_motion->StartContinuousCalibration();
motion->calibrating = true;
motion->calibrated = false;
}
void Input::stop_joy_motion_sensors_calibration(int p_device) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
ERR_FAIL_COND_MSG(!motion->sensors_enabled, "Motion sensors are not enabled on the joypad.");
ERR_FAIL_COND_MSG(!motion->calibrating, "Calibration hasn't been started.");
motion->gamepad_motion->PauseContinuousCalibration();
motion->calibrating = false;
motion->calibrated = true;
}
void Input::clear_joy_motion_sensors_calibration(int p_device) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
// Calibration might be in progress and the developer or the user might want to reset it,
// so no need to stop the calibration.
motion->gamepad_motion->ResetContinuousCalibration();
}
Dictionary Input::get_joy_motion_sensors_calibration(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return Dictionary();
}
if (!motion->calibrated) {
return Dictionary();
}
float joy_gyro_offset_data[3];
motion->gamepad_motion->GetCalibrationOffset(joy_gyro_offset_data[0], joy_gyro_offset_data[1], joy_gyro_offset_data[2]);
Vector3 joy_gyro_offset(joy_gyro_offset_data[0], joy_gyro_offset_data[1], joy_gyro_offset_data[2]);
Dictionary result;
result["gyroscope_offset"] = joy_gyro_offset * M_PI / 180.0;
return result;
}
void Input::set_joy_motion_sensors_calibration(int p_device, const Dictionary &p_calibration_info) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
ERR_FAIL_COND_MSG(motion->calibrating, "Calibration is currently in progress.");
Vector3 gyro_offset = p_calibration_info.get("gyroscope_offset", Vector3()).operator Vector3() * 180.0 / M_PI;
motion->gamepad_motion->SetCalibrationOffset(gyro_offset.x, gyro_offset.y, gyro_offset.z, 1);
motion->calibrating = false;
motion->calibrated = true;
}
bool Input::is_joy_motion_sensors_calibrating(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return false;
}
return motion->calibrating;
}
bool Input::is_joy_motion_sensors_calibrated(int p_device) const {
_THREAD_SAFE_METHOD_
const MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return false;
}
return motion->calibrated;
}
void Input::set_joy_motion_sensors_rate(int p_device, float p_rate) {
_THREAD_SAFE_METHOD_
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
motion->sensor_data_rate = p_rate;
}
void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) {
_THREAD_SAFE_METHOD_
if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
@ -1469,6 +1682,22 @@ void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
joy_names[p_device].hat_current = (int)p_val;
}
void Input::joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope) {
_THREAD_SAFE_METHOD_
// TODO: events
MotionInfo *motion = joy_motion.getptr(p_device);
if (motion == nullptr) {
return;
}
Vector3 gyro_degrees = p_gyroscope * 180.0 / M_PI;
Vector3 accel_g = -p_accelerometer / STANDARD_GRAVITY;
uint64_t new_timestamp = OS::get_singleton()->get_ticks_msec();
float delta_time = (new_timestamp - motion->last_timestamp) / 1000.0f;
motion->last_timestamp = new_timestamp;
motion->gamepad_motion->ProcessMotion(gyro_degrees.x, gyro_degrees.y, gyro_degrees.z, accel_g.x, accel_g.y, accel_g.z, delta_time);
}
void Input::_button_event(int p_device, JoyButton p_index, bool p_pressed) {
Ref<InputEventJoypadButton> ievent;
ievent.instantiate();
@ -1520,6 +1749,16 @@ void Input::_update_joypad_features(int p_device) {
if (joypad->features->has_joy_light()) {
joypad->has_light = true;
}
if (joypad->features->has_joy_motion_sensors()) {
MotionInfo &motion = joy_motion[p_device];
if (!motion.gamepad_motion) {
motion.gamepad_motion = new GamepadMotion();
} else {
motion.gamepad_motion->Reset();
}
motion.last_timestamp = OS::get_singleton()->get_ticks_msec();
}
}
Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) {

View File

@ -38,6 +38,8 @@
#include "core/templates/rb_set.h"
#include "core/variant/typed_array.h"
class GamepadMotion;
class Input : public Object {
GDCLASS(Input, Object);
_THREAD_SAFE_CLASS_
@ -85,6 +87,9 @@ public:
virtual bool has_joy_light() const { return false; }
virtual void set_joy_light(const Color &p_color) {}
virtual bool has_joy_motion_sensors() const { return false; }
virtual void set_joy_motion_sensors_enabled(bool p_enable) {}
};
static constexpr int32_t JOYPADS_MAX = 16;
@ -157,6 +162,23 @@ private:
HashMap<int, VibrationInfo> joy_vibration;
struct MotionInfo {
bool sensors_enabled : 1;
bool calibrating : 1;
bool calibrated : 1;
float sensor_data_rate = 0.0f;
uint64_t last_timestamp = 0;
GamepadMotion *gamepad_motion = nullptr;
MotionInfo() {
sensors_enabled = false;
calibrating = false;
calibrated = false;
}
};
HashMap<int, MotionInfo> joy_motion;
struct VelocityTrack {
uint64_t last_tick = 0;
Vector2 velocity;
@ -363,6 +385,28 @@ public:
void set_joy_light(int p_device, const Color &p_color);
bool has_joy_light(int p_device) const;
Vector3 get_joy_accelerometer(int p_device) const;
Vector3 get_joy_gravity(int p_device) const;
Vector3 get_joy_gyroscope(int p_device) const;
void set_joy_motion_sensors_enabled(int p_device, bool p_enable);
bool is_joy_motion_sensors_enabled(int p_device) const;
bool has_joy_motion_sensors(int p_device) const;
float get_joy_motion_sensors_rate(int p_device) const;
void start_joy_motion_sensors_calibration(int p_device);
void stop_joy_motion_sensors_calibration(int p_device);
void clear_joy_motion_sensors_calibration(int p_device);
Dictionary get_joy_motion_sensors_calibration(int p_device) const;
void set_joy_motion_sensors_calibration(int p_device, const Dictionary &p_calibration_info);
bool is_joy_motion_sensors_calibrating(int p_device) const;
bool is_joy_motion_sensors_calibrated(int p_device) const;
void set_joy_motion_sensors_rate(int p_device, float p_rate);
void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0);
void stop_joy_vibration(int p_device);
void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0);
@ -388,6 +432,7 @@ public:
void joy_button(int p_device, JoyButton p_button, bool p_pressed);
void joy_axis(int p_device, JoyAxis p_axis, float p_value);
void joy_hat(int p_device, BitField<HatMask> p_val);
void joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope);
void add_joy_mapping(const String &p_mapping, bool p_update_existing = false);
void remove_joy_mapping(const String &p_guid);

View File

@ -38,6 +38,15 @@
Adds a new mapping entry (in SDL2 format) to the mapping database. Optionally update already connected devices.
</description>
</method>
<method name="clear_joy_motion_sensors_calibration" experimental="">
<return type="void" />
<param index="0" name="device" type="int" />
<description>
Clears the calibration information for the specified joypad's motion sensors, if it has any and if they were calibrated.
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="flush_buffered_events">
<return type="void" />
<description>
@ -109,6 +118,19 @@
[b]Note:[/b] For Android, [member ProjectSettings.input_devices/sensors/enable_gyroscope] must be enabled.
</description>
</method>
<method name="get_joy_accelerometer" qualifiers="const" experimental="">
<return type="Vector3" />
<param index="0" name="device" type="int" />
<description>
Returns the acceleration, including the force of gravity, in m/s² of the joypad's accelerometer sensor, if the joypad has one and it's currently enabled. Otherwise, the method returns [constant Vector3.ZERO]. See also [method get_joy_gravity] and [method set_joy_motion_sensors_enabled].
For a joypad held in front of you, the returned axes are defined as follows:
+X ... -X: left ... right;
+Y ... -Y: bottom ... top;
+Z ... -Z: farther ... closer.
The gravity part value is measured as a vector with length of [code]9.8[/code] away from the center of the Earth, which is a negative Y value.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="get_joy_axis" qualifiers="const">
<return type="float" />
<param index="0" name="device" type="int" />
@ -117,6 +139,19 @@
Returns the current value of the joypad axis at index [param axis].
</description>
</method>
<method name="get_joy_gravity" qualifiers="const" experimental="">
<return type="Vector3" />
<param index="0" name="device" type="int" />
<description>
Returns the gravity in m/s² of the joypad's accelerometer sensor, if the joypad has one and it's currently enabled. Otherwise, the method returns [constant Vector3.ZERO]. See also [method get_joy_accelerometer] and [method set_joy_motion_sensors_enabled].
For a joypad held in front of you, the returned axes are defined as follows:
+X ... -X: left ... right;
+Y ... -Y: bottom ... top;
+Z ... -Z: farther ... closer.
The gravity part value is measured as a vector with length of [code]9.8[/code] away from the center of the Earth, which is a negative Y value.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="get_joy_guid" qualifiers="const">
<return type="String" />
<param index="0" name="device" type="int" />
@ -125,6 +160,20 @@
On Windows, all XInput joypad GUIDs will be overridden by Godot to [code]__XINPUT_DEVICE__[/code], because their mappings are the same.
</description>
</method>
<method name="get_joy_gyroscope" qualifiers="const" experimental="">
<return type="Vector3" />
<param index="0" name="device" type="int" />
<description>
Returns the rotation rate in rad/s around a joypad's X, Y, and Z axes of the gyroscope sensor, if the joypad has one and it's currently enabled. Otherwise, the method returns [constant Vector3.ZERO]. See also [method set_joy_motion_sensors_enabled].
The rotation is positive in the counter-clockwise direction.
For a joypad held in front of you, the returned axes are defined as follows:
X: Angular speed around the X axis (pitch);
Y: Angular speed around the Y axis (yaw);
Z: Angular speed around the Z axis (roll).
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad gyroscope and gyroscope calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="get_joy_info" qualifiers="const">
<return type="Dictionary" />
<param index="0" name="device" type="int" />
@ -140,6 +189,25 @@
[b]Note:[/b] The returned dictionary is always empty on Android, iOS, visionOS, and Web.
</description>
</method>
<method name="get_joy_motion_sensors_calibration" qualifiers="const" experimental="">
<return type="Dictionary" />
<param index="0" name="device" type="int" />
<description>
Returns the calibration information about the specified joypad's motion sensors in the form of a [Dictionary], if it has any and if they have been calibrated, otherwise returns an empty [Dictionary].
The dictionary contains the following fields:
[code]gyroscope_offset[/code]: average offset in gyroscope values from [constant Vector2.ZERO] in rad/s.
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="get_joy_motion_sensors_rate" qualifiers="const" experimental="">
<return type="float" />
<param index="0" name="device" type="int" />
<description>
Returns the joypad's motion sensor rate in Hz, if the joypad has motion sensors and they're currently enabled. See also [method set_joy_motion_sensors_enabled].
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="get_joy_name">
<return type="String" />
<param index="0" name="device" type="int" />
@ -208,6 +276,14 @@
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="has_joy_motion_sensors" qualifiers="const" experimental="">
<return type="bool" />
<param index="0" name="device" type="int" />
<description>
Returns [code]true[/code] if the joypad has motion sensors (accelerometer and gyroscope).
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="is_action_just_pressed" qualifiers="const">
<return type="bool" />
<param index="0" name="action" type="StringName" />
@ -288,6 +364,33 @@
Returns [code]true[/code] if the system knows the specified device. This means that it sets all button and axis indices. Unknown joypads are not expected to match these constants, but you can still retrieve events from them.
</description>
</method>
<method name="is_joy_motion_sensors_calibrated" qualifiers="const" experimental="">
<return type="bool" />
<param index="0" name="device" type="int" />
<description>
Returns [code]true[/code] if the joypad's motion sensors have been calibrated.
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="is_joy_motion_sensors_calibrating" qualifiers="const" experimental="">
<return type="bool" />
<param index="0" name="device" type="int" />
<description>
Returns [code]true[/code] if the joypad's motion sensors are currently being calibrated.
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="is_joy_motion_sensors_enabled" qualifiers="const" experimental="">
<return type="bool" />
<param index="0" name="device" type="int" />
<description>
Returns [code]true[/code] if the requested joypad has motion sensors (accelerometer and gyroscope) and they are currently enabled. See also [method set_joy_motion_sensors_enabled] and [method has_joy_motion_sensors].
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="is_key_label_pressed" qualifiers="const">
<return type="bool" />
<param index="0" name="keycode" type="int" enum="Key" />
@ -407,6 +510,27 @@
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="set_joy_motion_sensors_calibration" experimental="">
<return type="void" />
<param index="0" name="device" type="int" />
<param index="1" name="calibration_info" type="Dictionary" />
<description>
Sets the specified joypad's calibration information. See also [method get_joy_motion_sensors_calibration].
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="set_joy_motion_sensors_enabled" experimental="">
<return type="void" />
<param index="0" name="device" type="int" />
<param index="1" name="enable" type="bool" />
<description>
Enables or disables the motion sensors (accelerometer and gyroscope), if available, on the specified joypad.
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
It's recommended to disable the motion sensors when they're no longer being used, because otherwise it might drain the controller battery faster.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="set_magnetometer">
<return type="void" />
<param index="0" name="value" type="Vector3" />
@ -424,6 +548,114 @@
[b]Note:[/b] Some 3rd party tools can contribute to the list of ignored devices. For example, [i]SteamInput[/i] creates virtual devices from physical devices for remapping purposes. To avoid handling the same input device twice, the original device is added to the ignore list.
</description>
</method>
<method name="start_joy_motion_sensors_calibration" experimental="">
<return type="void" />
<param index="0" name="device" type="int" />
<description>
Starts the process of calibrating the specified joypad's gyroscope, if it has one.
Once a joypad's gyroscope has been calibrated correctly (e.g. laying still on a table without being rotated), [method get_joy_gyroscope] will return values close or equal to [constant Vector3.ZERO] when the joypad is not being rotated.
Here's an example of how to use joypad gyroscope and gyroscope calibration in your games:
[codeblocks]
[gdscript]
const GYRO_SENSITIVITY = 10.0
func _ready():
# In this example we only use the first connected joypad (id 0).
if 0 not in Input.get_connected_joypads():
return
if not Input.has_joy_motion_sensors(0):
return
# We must enable the motion sensors before using them.
Input.set_joy_motion_sensors_enabled(0, true)
# (Tell the users here that they need to put their joypads on a flat surface and wait for confirmation.)
# Start the calibration process.
calibrate_motion()
func _process(delta):
# Only move the object if the joypad motion sensors are calibrated.
if Input.is_joy_motion_sensors_calibrated(0):
move_object(delta)
func calibrate_motion():
Input.start_joy_motion_sensors_calibration(0)
# Wait for some time
await get_tree().create_timer(1.0).timeout
Input.stop_joy_motion_sensors_calibration(0)
# The joypad is now calibrated.
func move_object(delta):
var object: Node3D = ... # Put your object here.
var gyro := Input.get_joy_gyroscope(0)
object.rotation.x -= -gyro.y * GYRO_SENSITIVITY * 0.5 * delta # Use rotation around the Y axis (yaw) here
object.rotation.y += -gyro.x * GYRO_SENSITIVITY * delta # Use rotation around the X axis (pitch) here
[/gdscript]
[csharp]
const double GYRO_SENSITIVITY = 10.0;
public override void _Ready()
{
// In this example we only use the first connected joypad (id 0).
if (!Input.GetConnectedJoypads().Has(0))
{
return;
}
if (!Input.HasJoyMotionSensors(0))
{
return;
}
// We must enable the accelerometer and the gyroscope before using them.
Input.SetJoyMotionSensorsEnabled(0, true);
// (Tell the users here that they need to put their joypads on a flat surface and wait for confirmation.)
// Start the calibration process.
CalibrateMotion();
}
public override void _Process(double delta)
{
// Only move the object if the joypad motion sensors are calibrated.
if (Input.IsJoyMotionSensorsCalibrated(0))
{
MoveObject(delta);
}
}
private void CalibrateMotion()
{
Input.StartJoyMotionSensorsCalibration(0);
// Wait for some time.
await ToSignal(GetTree().CreateTimer(1.0), "timeout");
Input.StopJoyMotionSensorsCalibration(0);
// The joypad is now calibrated.
}
private void MoveObject(double delta)
{
Node3D object = ... ; // Put your object here.
Vector3 gyro = Input.GetJoyGyroscope(0);
Vector3 rotation = object.Rotation;
rotation.X -= -gyro.Y * GYRO_SENSITIVITY * 0.5 * delta; // Use rotation around the Y axis (yaw) here
rotation.Y += -gyro.X * GYRO_SENSITIVITY * delta; // Use rotation around the X axis (pitch) here
object.Rotation = rotation;
}
[/csharp]
[/codeblocks]
[b]Note:[/b] Accelerometer sensor doesn't usually require calibration.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="start_joy_vibration">
<return type="void" />
<param index="0" name="device" type="int" />
@ -436,6 +668,15 @@
[b]Note:[/b] For macOS, vibration is only supported in macOS 11 and later.
</description>
</method>
<method name="stop_joy_motion_sensors_calibration" experimental="">
<return type="void" />
<param index="0" name="device" type="int" />
<description>
Stops the calibration process of the specified joypad's motion sensors.
See [method start_joy_motion_sensors_calibration] for an example on how to use joypad motion sensors and calibration in your games.
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
</description>
</method>
<method name="stop_joy_vibration">
<return type="void" />
<param index="0" name="device" type="int" />

View File

@ -169,6 +169,7 @@ void JoypadSDL::process_events() {
joypads[joy_id].sdl_instance_idx = sdl_event.jdevice.which;
joypads[joy_id].supports_force_feedback = SDL_GetBooleanProperty(propertiesID, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);
joypads[joy_id].guid = StringName(String(guid));
joypads[joy_id].supports_motion_sensors = SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL) && SDL_GamepadHasSensor(gamepad, SDL_SENSOR_GYRO);
sdl_instance_id_to_joypad_id.insert(sdl_event.jdevice.which, joy_id);
@ -198,6 +199,11 @@ void JoypadSDL::process_events() {
joypad_info);
Input::get_singleton()->set_joy_features(joy_id, &joypads[joy_id]);
if (joypads[joy_id].supports_motion_sensors) {
// Data rate for all sensors should be the same.
Input::get_singleton()->set_joy_motion_sensors_rate(joy_id, SDL_GetGamepadSensorDataRate(gamepad, SDL_SENSOR_ACCEL));
}
}
// An event for an attached joypad
} else if (sdl_event.type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && sdl_event.type < SDL_EVENT_FINGER_DOWN && sdl_instance_id_to_joypad_id.has(sdl_event.jdevice.which)) {
@ -272,6 +278,25 @@ void JoypadSDL::process_events() {
}
}
}
for (int i = 0; i < Input::JOYPADS_MAX; i++) {
Joypad &joy = joypads[i];
if (!joy.attached || !joy.supports_motion_sensors) {
continue;
}
SDL_Gamepad *gamepad = SDL_GetGamepadFromID(joy.sdl_instance_idx);
// gamepad should not be NULL since joy.supports_motion_sensors is true here.
float accel_data[3];
float gyro_data[3];
SDL_GetGamepadSensorData(gamepad, SDL_SENSOR_ACCEL, accel_data, 3);
SDL_GetGamepadSensorData(gamepad, SDL_SENSOR_GYRO, gyro_data, 3);
Input::get_singleton()->joy_motion_sensors(
i,
Vector3(-accel_data[0], -accel_data[1], -accel_data[2]),
Vector3(gyro_data[0], gyro_data[1], gyro_data[2]));
}
}
void JoypadSDL::close_joypad(int p_pad_idx) {
@ -301,6 +326,16 @@ void JoypadSDL::Joypad::set_joy_light(const Color &p_color) {
SDL_SetJoystickLED(get_sdl_joystick(), p_color.get_r8(), p_color.get_g8(), p_color.get_b8());
}
bool JoypadSDL::Joypad::has_joy_motion_sensors() const {
return supports_motion_sensors;
}
void JoypadSDL::Joypad::set_joy_motion_sensors_enabled(bool p_enable) {
SDL_Gamepad *gamepad = get_sdl_gamepad();
SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, p_enable);
SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_GYRO, p_enable);
}
SDL_Joystick *JoypadSDL::Joypad::get_sdl_joystick() const {
return SDL_GetJoystickFromID(sdl_instance_idx);
}

View File

@ -56,11 +56,15 @@ private:
SDL_JoystickID sdl_instance_idx;
bool supports_force_feedback = false;
bool supports_motion_sensors = false;
uint64_t ff_effect_timestamp = 0;
virtual bool has_joy_light() const override;
virtual void set_joy_light(const Color &p_color) override;
virtual bool has_joy_motion_sensors() const override;
virtual void set_joy_motion_sensors_enabled(bool p_enable) override;
SDL_Joystick *get_sdl_joystick() const;
SDL_Gamepad *get_sdl_gamepad() const;
};

15
thirdparty/README.md vendored
View File

@ -368,6 +368,21 @@ Files extracted from upstream source:
- `LICENSE.TXT` and `docs/FTL.TXT`
## gamepadmotionhelpers
- Upstream: https://github.com/JibbSmart/GamepadMotionHelpers
- Version: 39b578aacf34c3a1c584d8f7f194adc776f88055, 2023
- License: MIT
Files extracted from upstream source:
- `GamepadMotion.hpp`
- `LICENSE.TXT`
Patches:
- `0001-fix-warnings.patch` ([GH-111679](https://github.com/godotengine/godot/pull/111679))
## glad
- Upstream: https://github.com/Dav1dde/glad

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2023 Julian "Jibb" Smart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,37 @@
diff --git a/thirdparty/gamepadmotionhelpers/GamepadMotion.hpp b/thirdparty/gamepadmotionhelpers/GamepadMotion.hpp
index 02497ac953..7d89f86d7f 100644
--- a/thirdparty/gamepadmotionhelpers/GamepadMotion.hpp
+++ b/thirdparty/gamepadmotionhelpers/GamepadMotion.hpp
@@ -810,11 +810,6 @@ namespace GamepadMotionHelpers
{
if (MinMaxWindow.NumSamples >= minStillnessSamples && MinMaxWindow.TimeSampled >= minStillnessCorrectionTime)
{
- /*if (TimeSteadyStillness == 0.f)
- {
- printf("Still!\n");
- }/**/
-
TimeSteadyStillness = std::min(TimeSteadyStillness + deltaTime, stillnessCalibrationEaseInTime);
const float calibrationEaseIn = stillnessCalibrationEaseInTime <= 0.f ? 1.f : TimeSteadyStillness / stillnessCalibrationEaseInTime;
@@ -973,20 +968,11 @@ namespace GamepadMotionHelpers
// apply corrections
if (gyroAccelerationMag > sensorFusionAngularAccelerationThreshold || CalibrationData == nullptr)
{
- /*if (TimeSteadySensorFusion > 0.f)
- {
- printf("Shaken!\n");
- }/**/
TimeSteadySensorFusion = 0.f;
//printf("No calibration due to acceleration of %.4f\n", gyroAccelerationMag);
}
else
{
- /*if (TimeSteadySensorFusion == 0.f)
- {
- printf("Steady!\n");
- }/**/
-
TimeSteadySensorFusion = std::min(TimeSteadySensorFusion + deltaTime, sensorFusionCalibrationEaseInTime);
const float calibrationEaseIn = sensorFusionCalibrationEaseInTime <= 0.f ? 1.f : TimeSteadySensorFusion / sensorFusionCalibrationEaseInTime;
const Vec oldGyroBias = Vec(CalibrationData->X, CalibrationData->Y, CalibrationData->Z) / std::max((float)CalibrationData->NumSamples, 1.f);