Skip to content

Commit b1af133

Browse files
authored
Fix touchpad handling and change gyro calculation (shadps4-emu#3006)
* Change touchpad handling and orientation calculation * remove unnecessary includes in pad.cpp * remove the cmake command arguments * remove the weird file * try to fix formatting * limit new gyro and touchpad logic to controller 1 * remove cout * fix formatting and add the handle check to scePadRead * swap y and z back
1 parent bb19986 commit b1af133

File tree

3 files changed

+237
-73
lines changed

3 files changed

+237
-73
lines changed

src/core/libraries/pad/pad.cpp

Lines changed: 124 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
316316
pData[i].angularVelocity.y = states[i].angularVelocity.y;
317317
pData[i].angularVelocity.z = states[i].angularVelocity.z;
318318
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
319-
if (engine) {
319+
pData[i].acceleration.x = states[i].acceleration.x * 0.098;
320+
pData[i].acceleration.y = states[i].acceleration.y * 0.098;
321+
pData[i].acceleration.z = states[i].acceleration.z * 0.098;
322+
pData[i].angularVelocity.x = states[i].angularVelocity.x;
323+
pData[i].angularVelocity.y = states[i].angularVelocity.y;
324+
pData[i].angularVelocity.z = states[i].angularVelocity.z;
325+
326+
if (engine && handle == 1) {
320327
const auto gyro_poll_rate = engine->GetAccelPollRate();
321328
if (gyro_poll_rate != 0.0f) {
322-
GameController::CalculateOrientation(pData[i].acceleration,
323-
pData[i].angularVelocity,
324-
1.0f / gyro_poll_rate, pData[i].orientation);
329+
auto now = std::chrono::steady_clock::now();
330+
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
331+
now - controller->GetLastUpdate())
332+
.count() /
333+
1000000.0f;
334+
controller->SetLastUpdate(now);
335+
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
336+
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
337+
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
338+
deltaTime, lastOrientation, outputOrientation);
339+
pData[i].orientation = outputOrientation;
340+
controller->SetLastOrientation(outputOrientation);
325341
}
326342
}
343+
327344
pData[i].touchData.touchNum =
328345
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
346+
347+
if (handle == 1) {
348+
if (controller->GetTouchCount() >= 127) {
349+
controller->SetTouchCount(0);
350+
}
351+
352+
if (controller->GetSecondaryTouchCount() >= 127) {
353+
controller->SetSecondaryTouchCount(0);
354+
}
355+
356+
if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
357+
controller->SetTouchCount(controller->GetTouchCount() + 1);
358+
controller->SetSecondaryTouchCount(controller->GetTouchCount());
359+
} else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
360+
controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
361+
} else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
362+
if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
363+
controller->SetTouchCount(controller->GetSecondaryTouchCount());
364+
} else {
365+
if (controller->WasSecondaryTouchReset()) {
366+
controller->SetTouchCount(controller->GetSecondaryTouchCount());
367+
controller->UnsetSecondaryTouchResetBool();
368+
}
369+
}
370+
}
371+
372+
controller->SetPreviousTouchNum(pData->touchData.touchNum);
373+
374+
if (pData->touchData.touchNum == 1) {
375+
states[i].touchpad[0].ID = controller->GetTouchCount();
376+
states[i].touchpad[1].ID = 0;
377+
} else if (pData->touchData.touchNum == 2) {
378+
states[i].touchpad[0].ID = controller->GetTouchCount();
379+
states[i].touchpad[1].ID = controller->GetSecondaryTouchCount();
380+
}
381+
} else {
382+
states[i].touchpad[0].ID = 1;
383+
states[i].touchpad[1].ID = 2;
384+
}
385+
329386
pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
330387
pData[i].touchData.touch[0].y = states[i].touchpad[0].y;
331-
pData[i].touchData.touch[0].id = 1;
388+
pData[i].touchData.touch[0].id = states[i].touchpad[0].ID;
332389
pData[i].touchData.touch[1].x = states[i].touchpad[1].x;
333390
pData[i].touchData.touch[1].y = states[i].touchpad[1].y;
334-
pData[i].touchData.touch[1].id = 2;
391+
pData[i].touchData.touch[1].id = states[i].touchpad[1].ID;
335392
pData[i].connected = connected;
336393
pData[i].timestamp = states[i].time;
337394
pData[i].connectedCount = connected_count;
@@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
376433
pData->leftStick.x = state.axes[static_cast<int>(Input::Axis::LeftX)];
377434
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
378435
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
436+
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
379437
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
380438
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
381439
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
382-
pData->acceleration.x = state.acceleration.x;
383-
pData->acceleration.y = state.acceleration.y;
384-
pData->acceleration.z = state.acceleration.z;
440+
pData->acceleration.x = state.acceleration.x * 0.098;
441+
pData->acceleration.y = state.acceleration.y * 0.098;
442+
pData->acceleration.z = state.acceleration.z * 0.098;
385443
pData->angularVelocity.x = state.angularVelocity.x;
386444
pData->angularVelocity.y = state.angularVelocity.y;
387445
pData->angularVelocity.z = state.angularVelocity.z;
388446
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f};
389-
if (engine) {
447+
448+
// Only do this on handle 1 for now
449+
if (engine && handle == 1) {
390450
const auto gyro_poll_rate = engine->GetAccelPollRate();
391451
if (gyro_poll_rate != 0.0f) {
452+
auto now = std::chrono::steady_clock::now();
453+
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
454+
now - controller->GetLastUpdate())
455+
.count() /
456+
1000000.0f;
457+
controller->SetLastUpdate(now);
458+
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
459+
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
392460
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
393-
1.0f / gyro_poll_rate, pData->orientation);
461+
deltaTime, lastOrientation, outputOrientation);
462+
pData->orientation = outputOrientation;
463+
controller->SetLastOrientation(outputOrientation);
394464
}
395465
}
396466
pData->touchData.touchNum =
397467
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
468+
469+
// Only do this on handle 1 for now
470+
if (handle == 1) {
471+
if (controller->GetTouchCount() >= 127) {
472+
controller->SetTouchCount(0);
473+
}
474+
475+
if (controller->GetSecondaryTouchCount() >= 127) {
476+
controller->SetSecondaryTouchCount(0);
477+
}
478+
479+
if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
480+
controller->SetTouchCount(controller->GetTouchCount() + 1);
481+
controller->SetSecondaryTouchCount(controller->GetTouchCount());
482+
} else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
483+
controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
484+
} else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
485+
if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
486+
controller->SetTouchCount(controller->GetSecondaryTouchCount());
487+
} else {
488+
if (controller->WasSecondaryTouchReset()) {
489+
controller->SetTouchCount(controller->GetSecondaryTouchCount());
490+
controller->UnsetSecondaryTouchResetBool();
491+
}
492+
}
493+
}
494+
495+
controller->SetPreviousTouchNum(pData->touchData.touchNum);
496+
497+
if (pData->touchData.touchNum == 1) {
498+
state.touchpad[0].ID = controller->GetTouchCount();
499+
state.touchpad[1].ID = 0;
500+
} else if (pData->touchData.touchNum == 2) {
501+
state.touchpad[0].ID = controller->GetTouchCount();
502+
state.touchpad[1].ID = controller->GetSecondaryTouchCount();
503+
}
504+
} else {
505+
state.touchpad[0].ID = 1;
506+
state.touchpad[1].ID = 2;
507+
}
508+
398509
pData->touchData.touch[0].x = state.touchpad[0].x;
399510
pData->touchData.touch[0].y = state.touchpad[0].y;
400-
pData->touchData.touch[0].id = 1;
511+
pData->touchData.touch[0].id = state.touchpad[0].ID;
401512
pData->touchData.touch[1].x = state.touchpad[1].x;
402513
pData->touchData.touch[1].y = state.touchpad[1].y;
403-
pData->touchData.touch[1].id = 2;
514+
pData->touchData.touch[1].id = state.touchpad[1].ID;
404515
pData->timestamp = state.time;
405516
pData->connected = true; // isConnected; //TODO fix me proper
406517
pData->connectedCount = 1; // connectedCount;

src/input/controller.cpp

Lines changed: 91 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
1+
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
22
// SPDX-License-Identifier: GPL-2.0-or-later
33

44
#include <SDL3/SDL.h>
@@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
165165
AddState(state);
166166
}
167167

168-
// Stolen from
169-
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
170-
float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
171-
const float Kp = 50.0f; // Proportional gain
172-
const float Ki = 1.0f; // Integral gain
173-
Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
174168
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
175169
Libraries::Pad::OrbisFVector3& angularVelocity,
176170
float deltaTime,
171+
Libraries::Pad::OrbisFQuaternion& lastOrientation,
177172
Libraries::Pad::OrbisFQuaternion& orientation) {
178-
float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
179-
float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
180-
181-
float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z;
182-
183-
// Normalize accelerometer measurement
184-
float norm = std::sqrt(ax * ax + ay * ay + az * az);
185-
if (norm == 0.0f || deltaTime == 0.0f)
186-
return; // Handle NaN
187-
norm = 1.0f / norm;
188-
ax *= norm;
189-
ay *= norm;
190-
az *= norm;
191-
192-
// Estimated direction of gravity
193-
float vx = 2.0f * (q2 * q4 - q1 * q3);
194-
float vy = 2.0f * (q1 * q2 + q3 * q4);
195-
float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
196-
197-
// Error is cross product between estimated direction and measured direction of gravity
198-
float ex = (ay * vz - az * vy);
199-
float ey = (az * vx - ax * vz);
200-
float ez = (ax * vy - ay * vx);
201-
if (Ki > 0.0f) {
202-
eInt[0] += ex * deltaTime; // Accumulate integral error
203-
eInt[1] += ey * deltaTime;
204-
eInt[2] += ez * deltaTime;
205-
} else {
206-
eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
207-
}
208-
209-
// Apply feedback terms
210-
gx += Kp * ex + Ki * eInt[0];
211-
gy += Kp * ey + Ki * eInt[1];
212-
gz += Kp * ez + Ki * eInt[2];
213-
214-
//// Integrate rate of change of quaternion
215-
q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
216-
q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
217-
q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
218-
q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
219-
220-
// Normalize quaternion
221-
norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
222-
norm = 1.0f / norm;
223-
orientation.w = q1 * norm;
224-
orientation.x = q2 * norm;
225-
orientation.y = q3 * norm;
226-
orientation.z = q4 * norm;
227-
o.w = q1 * norm;
228-
o.x = q2 * norm;
229-
o.y = q3 * norm;
230-
o.z = q4 * norm;
173+
Libraries::Pad::OrbisFQuaternion q = lastOrientation;
174+
Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z,
175+
0.0f};
176+
177+
Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y,
178+
q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z,
179+
q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x,
180+
q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z};
181+
182+
Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w};
183+
184+
q.x += qDot.x * deltaTime;
185+
q.y += qDot.y * deltaTime;
186+
q.z += qDot.z * deltaTime;
187+
q.w += qDot.w * deltaTime;
188+
189+
float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
190+
q.x /= norm;
191+
q.y /= norm;
192+
q.z /= norm;
193+
q.w /= norm;
194+
195+
orientation.x = q.x;
196+
orientation.y = q.y;
197+
orientation.z = q.z;
198+
orientation.w = q.w;
231199
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
232200
orientation.y, orientation.z, orientation.w);
233201
}
@@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f
260228
}
261229
}
262230

231+
u8 GameController::GetTouchCount() {
232+
std::scoped_lock lock{m_mutex};
233+
return m_touch_count;
234+
}
235+
236+
void GameController::SetTouchCount(u8 touchCount) {
237+
std::scoped_lock lock{m_mutex};
238+
m_touch_count = touchCount;
239+
}
240+
241+
u8 GameController::GetSecondaryTouchCount() {
242+
std::scoped_lock lock{m_mutex};
243+
return m_secondary_touch_count;
244+
}
245+
246+
void GameController::SetSecondaryTouchCount(u8 touchCount) {
247+
std::scoped_lock lock{m_mutex};
248+
m_secondary_touch_count = touchCount;
249+
if (touchCount == 0) {
250+
m_was_secondary_reset = true;
251+
}
252+
}
253+
254+
u8 GameController::GetPreviousTouchNum() {
255+
std::scoped_lock lock{m_mutex};
256+
return m_previous_touchnum;
257+
}
258+
259+
void GameController::SetPreviousTouchNum(u8 touchNum) {
260+
std::scoped_lock lock{m_mutex};
261+
m_previous_touchnum = touchNum;
262+
}
263+
264+
bool GameController::WasSecondaryTouchReset() {
265+
std::scoped_lock lock{m_mutex};
266+
return m_was_secondary_reset;
267+
}
268+
269+
void GameController::UnsetSecondaryTouchResetBool() {
270+
std::scoped_lock lock{m_mutex};
271+
m_was_secondary_reset = false;
272+
}
273+
274+
void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) {
275+
std::scoped_lock lock{m_mutex};
276+
m_orientation = orientation;
277+
}
278+
279+
Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() {
280+
std::scoped_lock lock{m_mutex};
281+
return m_orientation;
282+
}
283+
284+
std::chrono::steady_clock::time_point GameController::GetLastUpdate() {
285+
std::scoped_lock lock{m_mutex};
286+
return m_last_update;
287+
}
288+
289+
void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) {
290+
std::scoped_lock lock{m_mutex};
291+
m_last_update = lastUpdate;
292+
}
293+
263294
void GameController::SetEngine(std::unique_ptr<Engine> engine) {
264295
std::scoped_lock _{m_mutex};
265296
m_engine = std::move(engine);

src/input/controller.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum class Axis {
2323
};
2424

2525
struct TouchpadEntry {
26+
u8 ID = 0;
2627
bool state{};
2728
u16 x{};
2829
u16 y{};
@@ -82,9 +83,23 @@ class GameController {
8283
Engine* GetEngine();
8384
u32 Poll();
8485

86+
u8 GetTouchCount();
87+
void SetTouchCount(u8 touchCount);
88+
u8 GetSecondaryTouchCount();
89+
void SetSecondaryTouchCount(u8 touchCount);
90+
u8 GetPreviousTouchNum();
91+
void SetPreviousTouchNum(u8 touchNum);
92+
bool WasSecondaryTouchReset();
93+
void UnsetSecondaryTouchResetBool();
94+
95+
void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
96+
Libraries::Pad::OrbisFQuaternion GetLastOrientation();
97+
std::chrono::steady_clock::time_point GetLastUpdate();
98+
void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
8599
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
86100
Libraries::Pad::OrbisFVector3& angularVelocity,
87101
float deltaTime,
102+
Libraries::Pad::OrbisFQuaternion& lastOrientation,
88103
Libraries::Pad::OrbisFQuaternion& orientation);
89104

90105
private:
@@ -98,8 +113,15 @@ class GameController {
98113
int m_connected_count = 0;
99114
u32 m_states_num = 0;
100115
u32 m_first_state = 0;
116+
u8 m_touch_count = 0;
117+
u8 m_secondary_touch_count = 0;
118+
u8 m_previous_touch_count = 0;
119+
u8 m_previous_touchnum = 0;
120+
bool m_was_secondary_reset = false;
101121
std::array<State, MAX_STATES> m_states;
102122
std::array<StateInternal, MAX_STATES> m_private;
123+
std::chrono::steady_clock::time_point m_last_update = {};
124+
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
103125

104126
std::unique_ptr<Engine> m_engine = nullptr;
105127
};

0 commit comments

Comments
 (0)