mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-05 07:49:22 +00:00
[DesktopVRSwitch] - > DesktopXRSwitch
This commit is contained in:
parent
37fcf6ece1
commit
05374459be
21 changed files with 441 additions and 352 deletions
8
DesktopXRSwitch/DesktopXRSwitch.csproj
Normal file
8
DesktopXRSwitch/DesktopXRSwitch.csproj
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<Reference Include="Unity.XR.Management">
|
||||
<HintPath>..\_ManagedLibs\Unity.XR.Management.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
135
DesktopXRSwitch/DesktopXRSwitcher.cs
Normal file
135
DesktopXRSwitch/DesktopXRSwitcher.cs
Normal file
|
@ -0,0 +1,135 @@
|
|||
using NAK.Melons.DesktopXRSwitch.Patches;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Management;
|
||||
using Valve.VR;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch;
|
||||
|
||||
public class DesktopXRSwitcher : MonoBehaviour
|
||||
{
|
||||
//Debug Settings
|
||||
public bool _reloadLocalAvatar = true;
|
||||
public bool _softVRSwitch = false;
|
||||
|
||||
//Internal Stuff
|
||||
private bool _switchInProgress = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
//do not pause game, this breaks dynbones & trackers
|
||||
SteamVR_Settings.instance.pauseGameWhenDashboardVisible = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.F6) && Input.GetKey(KeyCode.LeftControl))
|
||||
{
|
||||
SwitchXRMode();
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchXRMode()
|
||||
{
|
||||
if (_switchInProgress) return;
|
||||
if (!IsInXR())
|
||||
{
|
||||
StartCoroutine(StartXRSystem());
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(StopXR());
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInXR() => XRGeneralSettings.Instance.Manager.activeLoader != null;
|
||||
|
||||
private IEnumerator StartXRSystem()
|
||||
{
|
||||
PreXRModeSwitch(true);
|
||||
yield return null;
|
||||
yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
|
||||
if (XRGeneralSettings.Instance.Manager.activeLoader != null)
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Starting OpenXR...");
|
||||
XRGeneralSettings.Instance.Manager.StartSubsystems();
|
||||
yield return null;
|
||||
PostXRModeSwitch(true);
|
||||
yield break;
|
||||
}
|
||||
DesktopXRSwitch.Logger.Error("Initializing XR Failed. Is there no XR device connected?");
|
||||
FailedVRModeSwitch(true);
|
||||
yield break;
|
||||
}
|
||||
|
||||
private IEnumerator StopXR()
|
||||
{
|
||||
PreXRModeSwitch(false);
|
||||
yield return null;
|
||||
if (XRGeneralSettings.Instance.Manager.isInitializationComplete)
|
||||
{
|
||||
XRGeneralSettings.Instance.Manager.StopSubsystems();
|
||||
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
|
||||
yield return null;
|
||||
PostXRModeSwitch(false);
|
||||
yield break;
|
||||
}
|
||||
DesktopXRSwitch.Logger.Error("Attempted to exit VR without a VR device loaded.");
|
||||
FailedVRModeSwitch(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
//one frame after switch attempt
|
||||
public void FailedVRModeSwitch(bool isXR)
|
||||
{
|
||||
if (_softVRSwitch) return;
|
||||
//let tracked objects know a switch failed
|
||||
XRModeSwitchTracker.FailXRModeSwitch(isXR);
|
||||
}
|
||||
|
||||
//one frame before switch attempt
|
||||
public void PreXRModeSwitch(bool isXR)
|
||||
{
|
||||
if (_softVRSwitch) return;
|
||||
//let tracked objects know we are attempting to switch
|
||||
XRModeSwitchTracker.PreXRModeSwitch(isXR);
|
||||
}
|
||||
|
||||
//one frame after switch attempt
|
||||
public void PostXRModeSwitch(bool isXR)
|
||||
{
|
||||
if (_softVRSwitch) return;
|
||||
|
||||
//close the menus
|
||||
TryCatchHell.CloseCohtmlMenus();
|
||||
|
||||
//the base of VR checks
|
||||
TryCatchHell.SetCheckVR(isXR);
|
||||
TryCatchHell.SetMetaPort(isXR);
|
||||
|
||||
//game basics for functional gameplay post switch
|
||||
TryCatchHell.RepositionCohtmlHud(isXR);
|
||||
TryCatchHell.UpdateHudOperations(isXR);
|
||||
TryCatchHell.DisableMirrorCanvas();
|
||||
TryCatchHell.SwitchActiveCameraRigs(isXR);
|
||||
TryCatchHell.SwitchCVRInputManager(isXR);
|
||||
TryCatchHell.UpdateRichPresence();
|
||||
TryCatchHell.UpdateGestureReconizerCam();
|
||||
|
||||
//let tracked objects know we switched
|
||||
XRModeSwitchTracker.PostXRModeSwitch(isXR);
|
||||
|
||||
//reload avatar by default, optional for debugging
|
||||
if (_reloadLocalAvatar)
|
||||
{
|
||||
TryCatchHell.ReloadLocalAvatar();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
_switchInProgress = false;
|
||||
}
|
||||
}
|
||||
|
93
DesktopXRSwitch/HarmonyPatches.cs
Normal file
93
DesktopXRSwitch/HarmonyPatches.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.Util.Object_Behaviour;
|
||||
using ABI_RC.Systems.IK;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using HarmonyLib;
|
||||
using NAK.Melons.DesktopXRSwitch.Patches;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.HarmonyPatches;
|
||||
|
||||
internal class PlayerSetupPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), "Start")]
|
||||
private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance)
|
||||
{
|
||||
__instance.gameObject.AddComponent<PlayerSetupTracker>();
|
||||
if (CheckVR.Instance != null)
|
||||
{
|
||||
CheckVR.Instance.gameObject.AddComponent<DesktopXRSwitcher>();
|
||||
return;
|
||||
}
|
||||
__instance.gameObject.AddComponent<DesktopXRSwitcher>();
|
||||
DesktopXRSwitch.Logger.Error("CheckVR not found. Reverting to fallback method. This should never happen!");
|
||||
}
|
||||
}
|
||||
|
||||
internal class MovementSystemPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(MovementSystem), "Start")]
|
||||
private static void Postfix_MovementSystem_Start(ref MovementSystem __instance)
|
||||
{
|
||||
__instance.gameObject.AddComponent<MovementSystemTracker>();
|
||||
}
|
||||
}
|
||||
|
||||
internal class CVRPickupObjectPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CVRPickupObject), "Start")]
|
||||
private static void Prefix_CVRPickupObject_Start(ref CVRPickupObject __instance)
|
||||
{
|
||||
if (__instance.gripType == CVRPickupObject.GripType.Free) return;
|
||||
Transform vrOrigin = __instance.gripOrigin;
|
||||
Transform desktopOrigin = __instance.gripOrigin.Find("[Desktop]");
|
||||
if (vrOrigin != null && desktopOrigin != null)
|
||||
{
|
||||
var tracker = __instance.gameObject.AddComponent<CVRPickupObjectTracker>();
|
||||
tracker.pickupObject = __instance;
|
||||
tracker.storedGripOrigin = (!MetaPort.Instance.isUsingVr ? vrOrigin : desktopOrigin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CVRWorldPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CVRWorld), "SetDefaultCamValues")]
|
||||
private static void CVRWorld_SetDefaultCamValues_Postfix()
|
||||
{
|
||||
ReferenceCameraPatch.OnWorldLoad();
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CVRWorld), "CopyRefCamValues")]
|
||||
private static void CVRWorld_CopyRefCamValues_Postfix()
|
||||
{
|
||||
ReferenceCameraPatch.OnWorldLoad();
|
||||
}
|
||||
}
|
||||
|
||||
internal class CameraFacingObjectPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CameraFacingObject), "Start")]
|
||||
private static void Postfix_CameraFacingObject_Start(ref CameraFacingObject __instance)
|
||||
{
|
||||
__instance.gameObject.AddComponent<CameraFacingObjectTracker>();
|
||||
}
|
||||
}
|
||||
|
||||
internal class IKSystemPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(IKSystem), "Start")]
|
||||
private static void Postfix_IKSystem_Start(ref IKSystem __instance)
|
||||
{
|
||||
__instance.gameObject.AddComponent<IKSystemTracker>();
|
||||
}
|
||||
}
|
51
DesktopXRSwitch/Main.cs
Normal file
51
DesktopXRSwitch/Main.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using MelonLoader;
|
||||
|
||||
/**
|
||||
I know the TryCatchHell thing might be a bit exessive, but it is
|
||||
built so if a user that happens to have access to a build I do not,
|
||||
I will have a good idea of what broke and where, and what to look out
|
||||
for when updates/experimentals release. (which has happened a few times)
|
||||
|
||||
It is also just in case other mods break or tweak functionality that
|
||||
could fuck with switching. Or if they try to detect switching and break...
|
||||
|
||||
The XRModeSwitchTracker system is also built so I can easily & quickly make adjustments to
|
||||
components that may or may not change between builds without breaking the rest of the mod.
|
||||
**/
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch;
|
||||
|
||||
public class DesktopXRSwitch : MelonMod
|
||||
{
|
||||
internal static MelonLogger.Instance Logger;
|
||||
|
||||
public static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(nameof(DesktopXRSwitch));
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryEnterCalibrationOnSwitch =
|
||||
Category.CreateEntry("Enter Calibration on Switch", true, description: "Should you automatically be placed into calibration after switch if FBT is available? Overridden by Save Calibration IK setting.");
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Logger = LoggerInstance;
|
||||
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.CVRPickupObjectPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.CVRWorldPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.CameraFacingObjectPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.IKSystemPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.MovementSystemPatches));
|
||||
}
|
||||
|
||||
private void ApplyPatches(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
HarmonyInstance.PatchAll(type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Msg($"Failed while patching {type.Name}!");
|
||||
Logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
31
DesktopXRSwitch/Patches/CVRPickupObjectTracker.cs
Normal file
31
DesktopXRSwitch/Patches/CVRPickupObjectTracker.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
|
||||
//Thanks Ben! I was scared of transpiler so I reworked a bit...
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
public class CVRPickupObjectTracker : MonoBehaviour
|
||||
{
|
||||
public CVRPickupObject pickupObject;
|
||||
public Transform storedGripOrigin;
|
||||
|
||||
void Start()
|
||||
{
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch += PostXRModeSwitch;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch -= PostXRModeSwitch;
|
||||
}
|
||||
|
||||
public void PostXRModeSwitch(bool isXR, Camera activeCamera)
|
||||
{
|
||||
if (pickupObject != null)
|
||||
{
|
||||
if (pickupObject._controllerRay != null) pickupObject._controllerRay.DropObject(true);
|
||||
(storedGripOrigin, pickupObject.gripOrigin) = (pickupObject.gripOrigin, storedGripOrigin);
|
||||
}
|
||||
}
|
||||
}
|
24
DesktopXRSwitch/Patches/CameraFacingObjectTracker.cs
Normal file
24
DesktopXRSwitch/Patches/CameraFacingObjectTracker.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using ABI_RC.Core.Util.Object_Behaviour;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
public class CameraFacingObjectTracker : MonoBehaviour
|
||||
{
|
||||
public CameraFacingObject cameraFacingObject;
|
||||
void Start()
|
||||
{
|
||||
cameraFacingObject = GetComponent<CameraFacingObject>();
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch += PostXRModeSwitch;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch -= PostXRModeSwitch;
|
||||
}
|
||||
|
||||
public void PostXRModeSwitch(bool isXR, Camera activeCamera)
|
||||
{
|
||||
cameraFacingObject.m_Camera = activeCamera;
|
||||
}
|
||||
}
|
89
DesktopXRSwitch/Patches/IKSystemTracker.cs
Normal file
89
DesktopXRSwitch/Patches/IKSystemTracker.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
using ABI_RC.Systems.IK;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using ABI_RC.Systems.IK.TrackingModules;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
public class IKSystemTracker : MonoBehaviour
|
||||
{
|
||||
public IKSystem ikSystem;
|
||||
|
||||
void Start()
|
||||
{
|
||||
ikSystem = GetComponent<IKSystem>();
|
||||
XRModeSwitchTracker.OnPreXRModeSwitch += PreXRModeSwitch;
|
||||
XRModeSwitchTracker.OnFailXRModeSwitch += FailedXRModeSwitch;
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch += PostXRModeSwitch;
|
||||
}
|
||||
void OnDestroy()
|
||||
{
|
||||
XRModeSwitchTracker.OnPreXRModeSwitch -= PreXRModeSwitch;
|
||||
XRModeSwitchTracker.OnFailXRModeSwitch -= FailedXRModeSwitch;
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch -= PostXRModeSwitch;
|
||||
}
|
||||
|
||||
public void PreXRModeSwitch(bool inXR, Camera activeCamera)
|
||||
{
|
||||
BodySystem.TrackingEnabled = false;
|
||||
BodySystem.TrackingPositionWeight = 0f;
|
||||
BodySystem.TrackingLocomotionEnabled = false;
|
||||
if (IKSystem.vrik != null)
|
||||
IKSystem.vrik.enabled = false;
|
||||
}
|
||||
|
||||
public void FailedXRModeSwitch(bool inXR, Camera activeCamera)
|
||||
{
|
||||
BodySystem.TrackingEnabled = true;
|
||||
BodySystem.TrackingPositionWeight = 1f;
|
||||
BodySystem.TrackingLocomotionEnabled = true;
|
||||
if (IKSystem.vrik != null)
|
||||
IKSystem.vrik.enabled = true;
|
||||
}
|
||||
|
||||
public void PostXRModeSwitch(bool inXR, Camera activeCamera)
|
||||
{
|
||||
if (IKSystem.vrik != null)
|
||||
DestroyImmediate(IKSystem.vrik);
|
||||
|
||||
//make sure you are fully tracking
|
||||
BodySystem.TrackingEnabled = true;
|
||||
BodySystem.TrackingPositionWeight = 1f;
|
||||
BodySystem.TrackingLocomotionEnabled = true;
|
||||
BodySystem.isCalibratedAsFullBody = false;
|
||||
BodySystem.isCalibrating = false;
|
||||
BodySystem.isRecalibration = false;
|
||||
//make it so you dont instantly end up in FBT from Desktop
|
||||
IKSystem.firstAvatarLoaded = DesktopXRSwitch.EntryEnterCalibrationOnSwitch.Value;
|
||||
//turn of finger tracking just in case user switched controllers
|
||||
ikSystem.FingerSystem.controlActive = false;
|
||||
|
||||
//vrik should be deleted by avatar switch
|
||||
|
||||
SetupOpenXRTrackingModule(inXR);
|
||||
}
|
||||
|
||||
void SetupOpenXRTrackingModule(bool enableVR)
|
||||
{
|
||||
var openXRTrackingModule = ikSystem._trackingModules.OfType<OpenXRTrackingModule>().FirstOrDefault();
|
||||
|
||||
if (openXRTrackingModule != null)
|
||||
{
|
||||
if (enableVR)
|
||||
{
|
||||
openXRTrackingModule.ModuleStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
openXRTrackingModule.ModuleDestroy();
|
||||
if (openXRTrackingModule != null)
|
||||
ikSystem._trackingModules.Remove(openXRTrackingModule);
|
||||
}
|
||||
}
|
||||
else if (enableVR)
|
||||
{
|
||||
var newVRModule = new OpenXRTrackingModule();
|
||||
ikSystem.AddTrackingModule(newVRModule);
|
||||
}
|
||||
}
|
||||
}
|
54
DesktopXRSwitch/Patches/MovementSystemTracker.cs
Normal file
54
DesktopXRSwitch/Patches/MovementSystemTracker.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using ABI_RC.Systems.MovementSystem;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
public class MovementSystemTracker : MonoBehaviour
|
||||
{
|
||||
public MovementSystem movementSystem;
|
||||
public Vector3 preSwitchWorldPosition;
|
||||
public Quaternion preSwitchWorldRotation;
|
||||
|
||||
void Start()
|
||||
{
|
||||
movementSystem = GetComponent<MovementSystem>();
|
||||
XRModeSwitchTracker.OnPreXRModeSwitch += PreXRModeSwitch;
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch += PostXRModeSwitch;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
XRModeSwitchTracker.OnPreXRModeSwitch -= PreXRModeSwitch;
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch -= PostXRModeSwitch;
|
||||
}
|
||||
|
||||
public void PreXRModeSwitch(bool isXR, Camera activeCamera)
|
||||
{
|
||||
//correct rotationPivot y position, so we dont teleport up/down
|
||||
Vector3 position = movementSystem.rotationPivot.transform.position;
|
||||
position.y = movementSystem.transform.position.y;
|
||||
preSwitchWorldPosition = position;
|
||||
preSwitchWorldRotation = movementSystem.rotationPivot.transform.rotation;
|
||||
//ChilloutVR does not use VRIK root right, so avatar root is VR player root.
|
||||
//This causes desync between VR and Desktop positions & collision on switch.
|
||||
|
||||
//I correct for this in lazy way, but i use rotationPivot instead of avatar root,
|
||||
//so the user can still switch even if avatar is null (if it failed to load for example).
|
||||
|
||||
movementSystem.ChangeCrouch(false);
|
||||
movementSystem.ChangeProne(false);
|
||||
}
|
||||
|
||||
public void PostXRModeSwitch(bool isXR, Camera activeCamera)
|
||||
{
|
||||
//immediatly update camera to new camera transform
|
||||
movementSystem.rotationPivot = activeCamera.transform;
|
||||
//lazy way of correcting Desktop & VR offset issue (game does the maths)
|
||||
movementSystem.TeleportToPosRot(preSwitchWorldPosition, preSwitchWorldRotation, false);
|
||||
//recenter desktop collision to player object
|
||||
if (!isXR) movementSystem.UpdateColliderCenter(movementSystem.transform.position);
|
||||
|
||||
movementSystem.ChangeCrouch(false);
|
||||
movementSystem.ChangeProne(false);
|
||||
}
|
||||
}
|
142
DesktopXRSwitch/Patches/PlayerSetupTracker.cs
Normal file
142
DesktopXRSwitch/Patches/PlayerSetupTracker.cs
Normal file
|
@ -0,0 +1,142 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Base;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK;
|
||||
using HarmonyLib;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
public class PlayerSetupTracker : MonoBehaviour
|
||||
{
|
||||
public static PlayerSetupTracker Instance;
|
||||
public PlayerSetup playerSetup;
|
||||
|
||||
public Traverse _initialCameraPosTraverse;
|
||||
public Traverse _lookIKTraverse;
|
||||
public Traverse _lastScaleTraverse;
|
||||
|
||||
public HumanPose avatarInitialHumanPose;
|
||||
public HumanPoseHandler avatarHumanPoseHandler;
|
||||
|
||||
public GameObject avatarObject;
|
||||
public CVRAvatar avatarDescriptor;
|
||||
public Animator avatarAnimator;
|
||||
public Vector3 initialPosition;
|
||||
public Quaternion initialRotation;
|
||||
|
||||
void Start()
|
||||
{
|
||||
Instance = this;
|
||||
playerSetup = GetComponent<PlayerSetup>();
|
||||
_initialCameraPosTraverse = Traverse.Create(playerSetup).Field("initialCameraPos");
|
||||
_lookIKTraverse = Traverse.Create(playerSetup).Field("lookIK");
|
||||
_lastScaleTraverse = Traverse.Create(playerSetup).Field("lastScale");
|
||||
}
|
||||
|
||||
public void OnSetupAvatar(GameObject avatar)
|
||||
{
|
||||
avatarObject = avatar;
|
||||
avatarDescriptor = avatarObject.GetComponent<CVRAvatar>();
|
||||
avatarAnimator = avatarObject.GetComponent<Animator>();
|
||||
initialPosition = avatarObject.transform.localPosition;
|
||||
initialRotation = avatarObject.transform.localRotation;
|
||||
if (avatarHumanPoseHandler == null)
|
||||
{
|
||||
avatarHumanPoseHandler = new HumanPoseHandler(avatarAnimator.avatar, avatarAnimator.transform);
|
||||
}
|
||||
avatarHumanPoseHandler.GetHumanPose(ref avatarInitialHumanPose);
|
||||
}
|
||||
|
||||
public void OnClearAvatar()
|
||||
{
|
||||
avatarObject = null;
|
||||
avatarDescriptor = null;
|
||||
avatarAnimator = null;
|
||||
initialPosition = Vector3.one;
|
||||
initialRotation = Quaternion.identity;
|
||||
avatarHumanPoseHandler = null;
|
||||
}
|
||||
|
||||
public void ConfigureAvatarIK(bool isVR)
|
||||
{
|
||||
bool StateOnDisable = avatarAnimator.keepAnimatorStateOnDisable;
|
||||
avatarAnimator.keepAnimatorStateOnDisable = true;
|
||||
avatarAnimator.enabled = false;
|
||||
|
||||
//reset avatar offsets
|
||||
avatarObject.transform.localPosition = initialPosition;
|
||||
avatarObject.transform.localRotation = initialRotation;
|
||||
avatarHumanPoseHandler.SetHumanPose(ref avatarInitialHumanPose);
|
||||
|
||||
if (isVR)
|
||||
{
|
||||
SwitchAvatarVr();
|
||||
}
|
||||
else
|
||||
{
|
||||
SwitchAvatarDesktop();
|
||||
}
|
||||
|
||||
//lazy fix
|
||||
_lastScaleTraverse.SetValue(Vector3.zero);
|
||||
MethodInfo setPlaySpaceScaleMethod = playerSetup.GetType().GetMethod("CheckUpdateAvatarScaleToPlaySpaceRelation", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
setPlaySpaceScaleMethod.Invoke(playerSetup, null);
|
||||
|
||||
avatarAnimator.keepAnimatorStateOnDisable = StateOnDisable;
|
||||
avatarAnimator.enabled = true;
|
||||
}
|
||||
|
||||
private void SwitchAvatarVr()
|
||||
{
|
||||
if (avatarDescriptor == null) return;
|
||||
|
||||
//remove LookAtIK if found
|
||||
LookAtIK avatarLookAtIK = avatarObject.GetComponent<LookAtIK>();
|
||||
if (avatarLookAtIK != null) DestroyImmediate(avatarLookAtIK);
|
||||
|
||||
//have IKSystem set up avatar for VR
|
||||
//a good chunk of IKSystem initialization checks for existing
|
||||
//stuff & if it is set up, so game is ready to handle it
|
||||
if (avatarAnimator != null && avatarAnimator.isHuman)
|
||||
{
|
||||
IKSystem.Instance.InitializeAvatar(avatarDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchAvatarDesktop()
|
||||
{
|
||||
if (avatarDescriptor == null) return;
|
||||
|
||||
//remove VRIK & VRIKRootController if found
|
||||
VRIK avatarVRIK = avatarObject.GetComponent<VRIK>();
|
||||
VRIKRootController avatarVRIKRootController = avatarObject.GetComponent<VRIKRootController>();
|
||||
if (avatarVRIK != null) DestroyImmediate(avatarVRIK);
|
||||
if (avatarVRIKRootController != null) DestroyImmediate(avatarVRIKRootController);
|
||||
|
||||
//remove all TwistRelaxer components
|
||||
TwistRelaxer[] avatarTwistRelaxers = avatarObject.GetComponentsInChildren<TwistRelaxer>();
|
||||
for (int i = 0; i < avatarTwistRelaxers.Length; i++)
|
||||
{
|
||||
DestroyImmediate(avatarTwistRelaxers[i]);
|
||||
}
|
||||
|
||||
Transform headTransform = avatarAnimator.GetBoneTransform(HumanBodyBones.Head);
|
||||
if (headTransform != null)
|
||||
{
|
||||
//set DesktopCameraRig to head bone pos
|
||||
playerSetup.desktopCameraRig.transform.position = headTransform.position;
|
||||
//add LookAtIK & Configure
|
||||
LookAtIK lookAtIK = avatarObject.AddComponentIfMissing<LookAtIK>();
|
||||
lookAtIK.solver.head = new IKSolverLookAt.LookAtBone(headTransform);
|
||||
lookAtIK.solver.headWeight = 0.75f;
|
||||
lookAtIK.solver.target = playerSetup.desktopCameraTarget.transform;
|
||||
_lookIKTraverse.SetValue(lookAtIK);
|
||||
}
|
||||
|
||||
//set camera position & initial position for headbob (both modes end up with same number)
|
||||
playerSetup.desktopCamera.transform.localPosition = (Vector3)_initialCameraPosTraverse.GetValue();
|
||||
}
|
||||
}
|
91
DesktopXRSwitch/Patches/ReferenceCameraPatch.cs
Normal file
91
DesktopXRSwitch/Patches/ReferenceCameraPatch.cs
Normal file
|
@ -0,0 +1,91 @@
|
|||
using ABI_RC.Core.Base;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using Aura2API;
|
||||
using BeautifyEffect;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AzureSky;
|
||||
using UnityEngine.Rendering.PostProcessing;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
internal class ReferenceCameraPatch
|
||||
{
|
||||
internal static void OnWorldLoad()
|
||||
{
|
||||
Camera activeCamera = (MetaPort.Instance.isUsingVr ? PlayerSetup.Instance.vrCamera : PlayerSetup.Instance.desktopCamera).GetComponent<Camera>();
|
||||
Camera inactiveCamera = (MetaPort.Instance.isUsingVr ? PlayerSetup.Instance.desktopCamera : PlayerSetup.Instance.vrCamera).GetComponent<Camera>();
|
||||
CopyToInactiveCam(activeCamera, inactiveCamera);
|
||||
}
|
||||
|
||||
internal static void CopyToInactiveCam(Camera activeCam, Camera inactiveCam)
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Copying active camera settings & components to inactive camera.");
|
||||
|
||||
//steal basic settings
|
||||
inactiveCam.farClipPlane = activeCam.farClipPlane;
|
||||
inactiveCam.nearClipPlane = activeCam.nearClipPlane;
|
||||
inactiveCam.cullingMask = activeCam.cullingMask;
|
||||
inactiveCam.depthTextureMode = activeCam.depthTextureMode;
|
||||
|
||||
//steal post processing if added
|
||||
PostProcessLayer ppLayerActiveCam = activeCam.GetComponent<PostProcessLayer>();
|
||||
PostProcessLayer ppLayerInactiveCam = inactiveCam.AddComponentIfMissing<PostProcessLayer>();
|
||||
if (ppLayerActiveCam != null && ppLayerInactiveCam != null)
|
||||
{
|
||||
ppLayerInactiveCam.enabled = ppLayerActiveCam.enabled;
|
||||
ppLayerInactiveCam.volumeLayer = ppLayerActiveCam.volumeLayer;
|
||||
}
|
||||
|
||||
//what even is this aura camera stuff
|
||||
AuraCamera auraActiveCam = activeCam.GetComponent<AuraCamera>();
|
||||
AuraCamera auraInactiveCam = inactiveCam.AddComponentIfMissing<AuraCamera>();
|
||||
if (auraActiveCam != null && auraInactiveCam != null)
|
||||
{
|
||||
auraInactiveCam.enabled = auraActiveCam.enabled;
|
||||
auraInactiveCam.frustumSettings = auraActiveCam.frustumSettings;
|
||||
}
|
||||
else
|
||||
{
|
||||
auraInactiveCam.enabled = false;
|
||||
}
|
||||
|
||||
//flare layer thing? the sun :_:_:_:_:_:
|
||||
FlareLayer flareActiveCam = activeCam.GetComponent<FlareLayer>();
|
||||
FlareLayer flareInactiveCam = inactiveCam.AddComponentIfMissing<FlareLayer>();
|
||||
if (flareActiveCam != null && flareInactiveCam != null)
|
||||
{
|
||||
flareInactiveCam.enabled = flareActiveCam.enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
flareInactiveCam.enabled = false;
|
||||
}
|
||||
|
||||
//and now what the fuck is fog scattering
|
||||
AzureFogScattering azureFogActiveCam = activeCam.GetComponent<AzureFogScattering>();
|
||||
AzureFogScattering azureFogInactiveCam = inactiveCam.AddComponentIfMissing<AzureFogScattering>();
|
||||
if (azureFogActiveCam != null && azureFogInactiveCam != null)
|
||||
{
|
||||
azureFogInactiveCam.fogScatteringMaterial = azureFogActiveCam.fogScatteringMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
Object.Destroy(inactiveCam.GetComponent<AzureFogScattering>());
|
||||
}
|
||||
|
||||
//why is there so many thingsssssssss
|
||||
Beautify beautifyActiveCam = activeCam.GetComponent<Beautify>();
|
||||
Beautify beautifyInactiveCam = inactiveCam.AddComponentIfMissing<Beautify>();
|
||||
if (beautifyActiveCam != null && beautifyInactiveCam != null)
|
||||
{
|
||||
beautifyInactiveCam.quality = beautifyActiveCam.quality;
|
||||
beautifyInactiveCam.profile = beautifyActiveCam.profile;
|
||||
}
|
||||
else
|
||||
{
|
||||
Object.Destroy(inactiveCam.gameObject.GetComponent<Beautify>());
|
||||
}
|
||||
}
|
||||
}
|
45
DesktopXRSwitch/Patches/VRModeSwitchTracker.cs
Normal file
45
DesktopXRSwitch/Patches/VRModeSwitchTracker.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Patches;
|
||||
|
||||
public class XRModeSwitchTracker
|
||||
{
|
||||
public static event UnityAction<bool, Camera> OnPreXRModeSwitch;
|
||||
public static event UnityAction<bool, Camera> OnPostXRModeSwitch;
|
||||
public static event UnityAction<bool, Camera> OnFailXRModeSwitch;
|
||||
|
||||
public static void PreXRModeSwitch(bool isXR)
|
||||
{
|
||||
TryCatchHell.TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Invoking XRModeSwitchTracker.OnPreXRModeSwitch.");
|
||||
Camera activeCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
|
||||
XRModeSwitchTracker.OnPreXRModeSwitch?.Invoke(isXR, activeCamera);
|
||||
},
|
||||
"Error while invoking XRModeSwitchTracker.OnPreXRModeSwitch. Did someone do a fucky?");
|
||||
}
|
||||
|
||||
public static void PostXRModeSwitch(bool isXR)
|
||||
{
|
||||
TryCatchHell.TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Invoking XRModeSwitchTracker.OnPostXRModeSwitch.");
|
||||
Camera activeCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
|
||||
XRModeSwitchTracker.OnPostXRModeSwitch?.Invoke(isXR, activeCamera);
|
||||
},
|
||||
"Error while invoking XRModeSwitchTracker.OnPostXRModeSwitch. Did someone do a fucky?");
|
||||
}
|
||||
|
||||
public static void FailXRModeSwitch(bool isXR)
|
||||
{
|
||||
TryCatchHell.TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Invoking XRModeSwitchTracker.OnFailXRModeSwitch.");
|
||||
Camera activeCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
|
||||
XRModeSwitchTracker.OnFailXRModeSwitch?.Invoke(isXR, activeCamera);
|
||||
},
|
||||
"Error while invoking OnFailXRModeSwitch.OnPreXRModeSwitch. Did someone do a fucky?");
|
||||
}
|
||||
}
|
30
DesktopXRSwitch/Properties/AssemblyInfo.cs
Normal file
30
DesktopXRSwitch/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using MelonLoader;
|
||||
using NAK.Melons.DesktopXRSwitch.Properties;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyTitle(nameof(NAK.Melons.DesktopXRSwitch))]
|
||||
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||
[assembly: AssemblyProduct(nameof(NAK.Melons.DesktopXRSwitch))]
|
||||
|
||||
[assembly: MelonInfo(
|
||||
typeof(NAK.Melons.DesktopXRSwitch.DesktopXRSwitch),
|
||||
nameof(NAK.Melons.DesktopXRSwitch),
|
||||
AssemblyInfoParams.Version,
|
||||
AssemblyInfoParams.Author,
|
||||
downloadLink: "https://github.com/NotAKidOnSteam/DesktopXRSwitch"
|
||||
)]
|
||||
|
||||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
[assembly: HarmonyDontPatchAll]
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch.Properties;
|
||||
internal static class AssemblyInfoParams
|
||||
{
|
||||
public const string Version = "4.3.4";
|
||||
public const string Author = "NotAKidoS";
|
||||
}
|
190
DesktopXRSwitch/TryCatchHell.cs
Normal file
190
DesktopXRSwitch/TryCatchHell.cs
Normal file
|
@ -0,0 +1,190 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.EventSystem;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.Camera;
|
||||
using ABI_RC.Systems.InputManagement;
|
||||
using ABI_RC.Systems.InputManagement.InputModules;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.Melons.DesktopXRSwitch;
|
||||
|
||||
internal class TryCatchHell
|
||||
{
|
||||
internal static void TryCatchWrapper(Action action, string errorMsg, params object[] msgArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DesktopXRSwitch.Logger.Error(string.Format(errorMsg, msgArgs));
|
||||
DesktopXRSwitch.Logger.Msg(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CloseCohtmlMenus()
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Closing ViewManager & CVR_MenuManager menus.");
|
||||
ViewManager.Instance.UiStateToggle(false);
|
||||
CVR_MenuManager.Instance.ToggleQuickMenu(false);
|
||||
},
|
||||
"Setting CheckVR hasVrDeviceLoaded failed.");
|
||||
}
|
||||
|
||||
internal static void SetCheckVR(bool isXR)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg($"Setting CheckVR hasVrDeviceLoaded to {isXR}.");
|
||||
CheckVR.Instance.hasVrDeviceLoaded = isXR;
|
||||
},
|
||||
"Setting CheckVR hasVrDeviceLoaded failed.");
|
||||
}
|
||||
|
||||
internal static void SetMetaPort(bool isXR)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg($"Setting MetaPort isUsingVr to {isXR}.");
|
||||
MetaPort.Instance.isUsingVr = isXR;
|
||||
},
|
||||
"Setting MetaPort isUsingVr failed.");
|
||||
}
|
||||
|
||||
internal static void RepositionCohtmlHud(bool isXR)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Configuring new hud affinity for CohtmlHud.");
|
||||
CohtmlHud.Instance.gameObject.transform.parent = isXR ? PlayerSetup.Instance.vrCamera.transform : PlayerSetup.Instance.desktopCamera.transform;
|
||||
CVRTools.ConfigureHudAffinity();
|
||||
CohtmlHud.Instance.gameObject.transform.localScale = new Vector3(1.2f, 1f, 1.2f);
|
||||
},
|
||||
"Error parenting CohtmlHud to active camera.");
|
||||
}
|
||||
|
||||
internal static void UpdateHudOperations(bool isXR)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Switching HudOperations worldLoadingItem & worldLoadStatus.");
|
||||
HudOperations.Instance.worldLoadingItem = isXR ? HudOperations.Instance.worldLoadingItemVr : HudOperations.Instance.worldLoadingItemDesktop;
|
||||
HudOperations.Instance.worldLoadStatus = isXR ? HudOperations.Instance.worldLoadStatusVr : HudOperations.Instance.worldLoadStatusDesktop;
|
||||
},
|
||||
"Failed switching HudOperations objects.");
|
||||
}
|
||||
|
||||
internal static void DisableMirrorCanvas()
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Forcing PortableCamera canvas mirroring off.");
|
||||
//tell the game we are in mirror mode so itll disable it (if enabled)
|
||||
PortableCamera.Instance.mode = MirroringMode.Mirror;
|
||||
PortableCamera.Instance.ChangeMirroring();
|
||||
},
|
||||
"Failed to disable PortableCamera canvas mirroring.");
|
||||
}
|
||||
|
||||
internal static void SwitchActiveCameraRigs(bool isXR)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Switching active PlayerSetup camera rigs. Updating Desktop camera FOV.");
|
||||
PlayerSetup.Instance.desktopCameraRig.SetActive(!isXR);
|
||||
PlayerSetup.Instance.vrCameraRig.SetActive(isXR);
|
||||
CVR_DesktopCameraController.UpdateFov();
|
||||
//uicamera has script that copies fov from desktop cam
|
||||
//toggling the cameras on/off resets aspect ratio
|
||||
//so when rigs switch, that is already handled
|
||||
},
|
||||
"Failed to switch active camera rigs or update Desktop camera FOV.");
|
||||
}
|
||||
|
||||
internal static void PauseInputInteractions(bool toggle)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg($"Setting CVRInputManager inputEnabled & CVR_InteractableManager enableInteractions to {!toggle}");
|
||||
CVRInputManager.Instance.inputEnabled = !toggle;
|
||||
CVR_InteractableManager.enableInteractions = !toggle;
|
||||
},
|
||||
"Failed to toggle CVRInputManager inputEnabled & CVR_InteractableManager enableInteractions.");
|
||||
}
|
||||
|
||||
internal static void SwitchCVRInputManager(bool isXR)
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Resetting CVRInputManager inputs.");
|
||||
//just in case
|
||||
CVRInputManager.Instance.blockedByUi = false;
|
||||
//sometimes head can get stuck, so just in case
|
||||
CVRInputManager.Instance.independentHeadToggle = false;
|
||||
//just nice to load into desktop with idle gesture
|
||||
CVRInputManager.Instance.gestureLeft = 0f;
|
||||
CVRInputManager.Instance.gestureLeftRaw = 0f;
|
||||
CVRInputManager.Instance.gestureRight = 0f;
|
||||
CVRInputManager.Instance.gestureRightRaw = 0f;
|
||||
//turn off finger tracking input
|
||||
CVRInputManager.Instance.individualFingerTracking = false;
|
||||
|
||||
//add input module if you started in desktop
|
||||
if (CVRInputManager._moduleOpenXR == null)
|
||||
{
|
||||
CVRInputManager.Instance.AddInputModule(CVRInputManager._moduleOpenXR = new CVRInputModule_OpenXR());
|
||||
}
|
||||
|
||||
//enable xr input or whatnot
|
||||
CVRInputManager._moduleOpenXR.InputEnabled = isXR;
|
||||
},
|
||||
"Failed to reset CVRInputManager inputs.");
|
||||
}
|
||||
|
||||
internal static void ReloadLocalAvatar()
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Attempting to reload current local avatar from GUID.");
|
||||
AssetManagement.Instance.LoadLocalAvatar(MetaPort.Instance.currentAvatarGuid);
|
||||
},
|
||||
"Failed to reload local avatar.");
|
||||
}
|
||||
|
||||
internal static void UpdateRichPresence()
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
if (MetaPort.Instance.settings.GetSettingsBool("ImplementationRichPresenceDiscordEnabled", true))
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Forcing Discord Rich Presence update.");
|
||||
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceDiscordEnabled", false);
|
||||
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceDiscordEnabled", true);
|
||||
}
|
||||
if (MetaPort.Instance.settings.GetSettingsBool("ImplementationRichPresenceSteamEnabled", true))
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Forcing Steam Rich Presence update.");
|
||||
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceSteamEnabled", false);
|
||||
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceSteamEnabled", true);
|
||||
}
|
||||
},
|
||||
"Failed to update Discord & Steam Rich Presence.");
|
||||
}
|
||||
|
||||
internal static void UpdateGestureReconizerCam()
|
||||
{
|
||||
TryCatchWrapper(() =>
|
||||
{
|
||||
DesktopXRSwitch.Logger.Msg("Updating CVRGestureRecognizer _camera to active camera.");
|
||||
Traverse.Create(CVRGestureRecognizer.Instance).Field("_camera").SetValue(PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>());
|
||||
},
|
||||
"Failed to update CVRGestureRecognizer camera.");
|
||||
}
|
||||
}
|
23
DesktopXRSwitch/format.json
Normal file
23
DesktopXRSwitch/format.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"_id": 103,
|
||||
"name": "DesktopVRSwitch",
|
||||
"modversion": "4.3.1",
|
||||
"gameversion": "2022r170",
|
||||
"loaderversion": "0.5.7",
|
||||
"modtype": "Mod",
|
||||
"author": "NotAKidoS",
|
||||
"description": "Allows you to switch between Desktop and VR with a keybind.\n**Press Control + F6 to switch.**\n\nWhile this mod is a nice convienence feature to have access to, not every chillout system or mod is built to support it. I cannot possibly cover every edge case or mitigate issues with every mod. **Use at your own discretion.**",
|
||||
"searchtags": [
|
||||
"desktop",
|
||||
"vr",
|
||||
"switch",
|
||||
"hotswap"
|
||||
],
|
||||
"requirements": [
|
||||
"None"
|
||||
],
|
||||
"downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRSwitch/releases/download/v4.3.1/DesktopVRSwitch.dll",
|
||||
"sourcelink": "https://github.com/NotAKidOnSteam/DesktopVRSwitch/",
|
||||
"changelog": "- Backported rewrite from Experimental & patched up to handle OpenVR.\n- Reinitialize SteamVR input on switch incase user restarted SteamVR or changed controllers. Correct fixedDeltaTime when entering Desktop.\n- Many tweaks to ensure everything in-game functions as expected.",
|
||||
"embedcolor": "3498db"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue