android - add motion support

This commit is contained in:
Emmanuel Hansen 2023-12-16 14:04:25 +00:00
parent 5538227fce
commit 27059ded86
8 changed files with 187 additions and 4 deletions

View File

@ -553,6 +553,20 @@ namespace LibRyujinx
SetButtonReleased((GamepadButtonInputId)(int)button, id); SetButtonReleased((GamepadButtonInputId)(int)button, id);
} }
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetAccelerometerData")]
public static void JniSetAccelerometerData(JEnvRef jEnv, JObjectLocalRef jObj, JFloat x, JFloat y, JFloat z, JInt id)
{
var accel = new Vector3(x, y, z);
SetAccelerometerData(accel, id);
}
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetGyroData")]
public static void JniSetGyroData(JEnvRef jEnv, JObjectLocalRef jObj, JFloat x, JFloat y, JFloat z, JInt id)
{
var gryo = new Vector3(x, y, z);
SetGryoData(gryo, id);
}
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetStickAxis")] [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetStickAxis")]
public static void JniSetStickAxis(JEnvRef jEnv, JObjectLocalRef jObj, JInt stick, JFloat x, JFloat y, JInt id) public static void JniSetStickAxis(JEnvRef jEnv, JObjectLocalRef jObj, JInt stick, JFloat x, JFloat y, JInt id)
{ {

View File

@ -1,4 +1,4 @@
using DiscordRPC; using DiscordRPC;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
@ -83,6 +83,16 @@ namespace LibRyujinx
_gamepadDriver?.SetButtonReleased(button, id); _gamepadDriver?.SetButtonReleased(button, id);
} }
public static void SetAccelerometerData(Vector3 accel, int id)
{
_gamepadDriver?.SetAccelerometerData(accel, id);
}
public static void SetGryoData(Vector3 gyro, int id)
{
_gamepadDriver?.SetGryoData(gyro, id);
}
public static void SetStickAxis(StickInputId stick, Vector2 axes, int deviceId) public static void SetStickAxis(StickInputId stick, Vector2 axes, int deviceId)
{ {
_gamepadDriver?.SetStickAxis(stick, axes, deviceId); _gamepadDriver?.SetStickAxis(stick, axes, deviceId);
@ -460,6 +470,22 @@ namespace LibRyujinx
gamePad.ButtonInputs[(int)button] = false; gamePad.ButtonInputs[(int)button] = false;
} }
} }
public void SetAccelerometerData(Vector3 accel, int deviceId)
{
if (_gamePads.TryGetValue(deviceId, out var gamePad))
{
gamePad.Accelerometer = accel;
}
}
public void SetGryoData(Vector3 gyro, int deviceId)
{
if (_gamePads.TryGetValue(deviceId, out var gamePad))
{
gamePad.Gyro = gyro;
}
}
} }
public class VirtualGamepad : IGamepad public class VirtualGamepad : IGamepad
@ -481,7 +507,7 @@ namespace LibRyujinx
public void Dispose() { } public void Dispose() { }
public GamepadFeaturesFlag Features { get; } public GamepadFeaturesFlag Features { get; } = GamepadFeaturesFlag.Motion;
public string Id { get; } public string Id { get; }
internal readonly int IdInt; internal readonly int IdInt;
@ -490,6 +516,8 @@ namespace LibRyujinx
public bool IsConnected { get; } public bool IsConnected { get; }
public Vector2[] StickInputs { get => _stickInputs; set => _stickInputs = value; } public Vector2[] StickInputs { get => _stickInputs; set => _stickInputs = value; }
public bool[] ButtonInputs { get => _buttonInputs; set => _buttonInputs = value; } public bool[] ButtonInputs { get => _buttonInputs; set => _buttonInputs = value; }
public Vector3 Accelerometer { get; internal set; }
public Vector3 Gyro { get; internal set; }
public bool IsPressed(GamepadButtonInputId inputId) public bool IsPressed(GamepadButtonInputId inputId)
{ {
@ -505,9 +533,18 @@ namespace LibRyujinx
public Vector3 GetMotionData(MotionInputId inputId) public Vector3 GetMotionData(MotionInputId inputId)
{ {
if (inputId == MotionInputId.Accelerometer)
return Accelerometer;
else if (inputId == MotionInputId.Gyroscope)
return RadToDegree(Gyro);
return new Vector3(); return new Vector3();
} }
private static Vector3 RadToDegree(Vector3 rad)
{
return rad * (180 / MathF.PI);
}
public void SetTriggerThreshold(float triggerThreshold) public void SetTriggerThreshold(float triggerThreshold)
{ {
//throw new System.NotImplementedException(); //throw new System.NotImplementedException();

View File

@ -96,7 +96,8 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
mainViewModel.controller?.connect() mainViewModel.controller?.connect()
} }
mainViewModel.physicalControllerManager?.connect() val id = mainViewModel.physicalControllerManager?.connect()
mainViewModel.motionSensorManager?.setControllerId(id ?: -1)
_nativeRyujinx.graphicsRendererSetSize( _nativeRyujinx.graphicsRendererSetSize(
surfaceHolder.surfaceFrame.width(), surfaceHolder.surfaceFrame.width(),

View File

@ -28,6 +28,7 @@ import kotlin.math.abs
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
private var physicalControllerManager: PhysicalControllerManager = private var physicalControllerManager: PhysicalControllerManager =
PhysicalControllerManager(this) PhysicalControllerManager(this)
private lateinit var motionSensorManager: MotionSensorManager
private var _isInit: Boolean = false private var _isInit: Boolean = false
var isGameRunning = false var isGameRunning = false
var storageHelper: SimpleStorageHelper? = null var storageHelper: SimpleStorageHelper? = null
@ -80,6 +81,8 @@ class MainActivity : BaseActivity() {
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
motionSensorManager = MotionSensorManager(this)
Thread.setDefaultUncaughtExceptionHandler(crashHandler) Thread.setDefaultUncaughtExceptionHandler(crashHandler)
if( if(
@ -97,6 +100,7 @@ class MainActivity : BaseActivity() {
mainViewModel = MainViewModel(this) mainViewModel = MainViewModel(this)
mainViewModel!!.physicalControllerManager = physicalControllerManager mainViewModel!!.physicalControllerManager = physicalControllerManager
mainViewModel!!.motionSensorManager = motionSensorManager
mainViewModel?.apply { mainViewModel?.apply {
setContent { setContent {
@ -194,6 +198,7 @@ class MainActivity : BaseActivity() {
setFullScreen(true) setFullScreen(true)
NativeHelpers.instance.setTurboMode(true) NativeHelpers.instance.setTurboMode(true)
force60HzRefreshRate(true) force60HzRefreshRate(true)
motionSensorManager.register()
} }
} }
@ -204,5 +209,7 @@ class MainActivity : BaseActivity() {
NativeHelpers.instance.setTurboMode(false) NativeHelpers.instance.setTurboMode(false)
force60HzRefreshRate(false) force60HzRefreshRate(false)
} }
motionSensorManager.unregister()
} }
} }

View File

@ -0,0 +1,111 @@
package org.ryujinx.android
import android.app.Activity
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener2
import android.hardware.SensorManager
import android.view.OrientationEventListener
class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
private var isRegistered: Boolean = false
private var gyro: Sensor?
private var accelerometer: Sensor?
private var sensorManager: SensorManager =
activity.getSystemService(Activity.SENSOR_SERVICE) as SensorManager
private var controllerId: Int = -1
private val motionGyroOrientation : FloatArray = FloatArray(3)
private val motionAcelOrientation : FloatArray = FloatArray(3)
init {
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
setOrientation90()
var orientationListener = object : OrientationEventListener(activity){
override fun onOrientationChanged(orientation: Int) {
when{
isWithinOrientationRange(orientation, 270) -> {
setOrientation270()
}
isWithinOrientationRange(orientation, 90) -> {
setOrientation90()
}
}
}
private fun isWithinOrientationRange(
currentOrientation : Int, targetOrientation : Int, epsilon : Int = 90
) : Boolean {
return currentOrientation > targetOrientation - epsilon
&& currentOrientation < targetOrientation + epsilon
}
}
}
fun setOrientation270() {
motionGyroOrientation[0] = -1.0f
motionGyroOrientation[1] = 1.0f
motionGyroOrientation[2] = 1.0f
motionAcelOrientation[0] = 1.0f
motionAcelOrientation[1] = -1.0f
motionAcelOrientation[2] = -1.0f
}
fun setOrientation90() {
motionGyroOrientation[0] = 1.0f
motionGyroOrientation[1] = -1.0f
motionGyroOrientation[2] = 1.0f
motionAcelOrientation[0] = -1.0f
motionAcelOrientation[1] = 1.0f
motionAcelOrientation[2] = -1.0f
}
fun setControllerId(id: Int){
controllerId = id
}
fun register(){
if(isRegistered)
return
gyro?.apply {
sensorManager.registerListener(this@MotionSensorManager, gyro, SensorManager.SENSOR_DELAY_GAME)
}
accelerometer?.apply {
sensorManager.registerListener(this@MotionSensorManager, accelerometer, SensorManager.SENSOR_DELAY_GAME)
}
isRegistered = true;
}
fun unregister(){
sensorManager.unregisterListener(this)
isRegistered = false
}
override fun onSensorChanged(event: SensorEvent?) {
if (controllerId != -1)
event?.apply {
when (sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
val x = motionAcelOrientation[0] * event.values[1]
val y = motionAcelOrientation[1] * event.values[0]
val z = motionAcelOrientation[2] * event.values[2]
RyujinxNative.instance.inputSetAccelerometerData(x, y, z, controllerId)
}
Sensor.TYPE_GYROSCOPE -> {
val x = motionGyroOrientation[0] * event.values[1]
val y = motionGyroOrientation[1] * event.values[0]
val z = motionGyroOrientation[2] * event.values[2]
RyujinxNative.instance.inputSetGyroData(x, y, z, controllerId)
}
}
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
}
override fun onFlushCompleted(sensor: Sensor?) {
}
}

View File

@ -41,8 +41,13 @@ class PhysicalControllerManager(val activity: MainActivity) {
} }
} }
fun connect(){ fun connect() : Int {
controllerId = ryujinxNative.inputConnectGamepad(0) controllerId = ryujinxNative.inputConnectGamepad(0)
return controllerId
}
fun disconnect(){
controllerId = -1
} }
private fun getGamePadButtonInputId(keycode: Int): GamePadButtonInputId { private fun getGamePadButtonInputId(keycode: Int): GamePadButtonInputId {

View File

@ -49,6 +49,8 @@ class RyujinxNative {
external fun inputSetButtonReleased(button: Int, id: Int) external fun inputSetButtonReleased(button: Int, id: Int)
external fun inputConnectGamepad(index: Int): Int external fun inputConnectGamepad(index: Int): Int
external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int) external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int)
external fun inputSetAccelerometerData(x: Float, y: Float, z: Float, id: Int)
external fun inputSetGyroData(x: Float, y: Float, z: Float, id: Int)
external fun graphicsSetSurface(surface: Long, window: Long) external fun graphicsSetSurface(surface: Long, window: Long)
external fun deviceCloseEmulation() external fun deviceCloseEmulation()
external fun deviceSignalEmulationClose() external fun deviceSignalEmulationClose()

View File

@ -14,6 +14,7 @@ import org.ryujinx.android.GameHost
import org.ryujinx.android.GraphicsConfiguration import org.ryujinx.android.GraphicsConfiguration
import org.ryujinx.android.Logging import org.ryujinx.android.Logging
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
import org.ryujinx.android.MotionSensorManager
import org.ryujinx.android.NativeGraphicsInterop import org.ryujinx.android.NativeGraphicsInterop
import org.ryujinx.android.NativeHelpers import org.ryujinx.android.NativeHelpers
import org.ryujinx.android.PerformanceManager import org.ryujinx.android.PerformanceManager
@ -26,6 +27,7 @@ import java.io.File
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
class MainViewModel(val activity: MainActivity) { class MainViewModel(val activity: MainActivity) {
var physicalControllerManager: PhysicalControllerManager? = null var physicalControllerManager: PhysicalControllerManager? = null
var motionSensorManager: MotionSensorManager? = null
var gameModel: GameModel? = null var gameModel: GameModel? = null
var controller: GameController? = null var controller: GameController? = null
var performanceManager: PerformanceManager? = null var performanceManager: PerformanceManager? = null
@ -62,6 +64,9 @@ class MainViewModel(val activity: MainActivity) {
RyujinxNative.instance.deviceSignalEmulationClose() RyujinxNative.instance.deviceSignalEmulationClose()
gameHost?.close() gameHost?.close()
RyujinxNative.instance.deviceCloseEmulation() RyujinxNative.instance.deviceCloseEmulation()
motionSensorManager?.unregister()
physicalControllerManager?.disconnect()
motionSensorManager?.setControllerId(-1)
} }
fun loadGame(game:GameModel) : Boolean { fun loadGame(game:GameModel) : Boolean {
@ -354,6 +359,7 @@ class MainViewModel(val activity: MainActivity) {
activity.setFullScreen(true) activity.setFullScreen(true)
navController?.navigate("game") navController?.navigate("game")
activity.isGameRunning = true activity.isGameRunning = true
motionSensorManager?.register()
} }
fun setProgressStates( fun setProgressStates(