Merge remote-tracking branch 'DesktopVRSwitch/cvr-stable-openvr'

Merge DesktopVRSwitch
This commit is contained in:
NotAKidoS 2023-04-16 07:06:36 -05:00
commit 6b313bc7bd
16 changed files with 1035 additions and 18 deletions

View file

@ -0,0 +1,141 @@
using NAK.Melons.DesktopVRSwitch.Patches;
using System.Collections;
using UnityEngine;
using UnityEngine.XR;
using Valve.VR;
namespace NAK.Melons.DesktopVRSwitch;
public class DesktopVRSwitch : 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))
{
SwitchVRMode();
}
}
public void SwitchVRMode()
{
if (_switchInProgress) return;
if (!IsInVR())
{
StartCoroutine(StartVRSystem());
}
else
{
StartCoroutine(StopVR());
}
}
public bool IsInVR() => XRSettings.enabled;
private IEnumerator StartVRSystem()
{
PreVRModeSwitch(true);
XRSettings.LoadDeviceByName("OpenVR");
yield return null; //wait a frame before checking
if (!string.IsNullOrEmpty(XRSettings.loadedDeviceName))
{
DesktopVRSwitchMod.Logger.Msg("Starting SteamVR...");
XRSettings.enabled = true;
//force steamvr to reinitialize input
//this does SteamVR_Input.actionSets[0].Activate() for us (we deactivate in StopVR())
//but only if SteamVR_Settings.instance.activateFirstActionSetOnStart is enabled
//which in ChilloutVR, it is, because all those settings are default
SteamVR_Input.Initialize(true);
yield return null;
PostVRModeSwitch(true);
yield break;
}
DesktopVRSwitchMod.Logger.Error("Initializing VR Failed. Is there no VR device connected?");
FailedVRModeSwitch(true);
yield break;
}
private IEnumerator StopVR()
{
PreVRModeSwitch(false);
yield return null;
if (!string.IsNullOrEmpty(XRSettings.loadedDeviceName))
{
//SteamVR.SafeDispose(); //might fuck with SteamVRTrackingModule
//deactivate the action set so SteamVR_Input.Initialize can reactivate
SteamVR_Input.actionSets[0].Deactivate(SteamVR_Input_Sources.Any);
XRSettings.LoadDeviceByName("");
XRSettings.enabled = false;
yield return null;
Time.fixedDeltaTime = 0.02f; //reset physics time to Desktop default
PostVRModeSwitch(false);
yield break;
}
DesktopVRSwitchMod.Logger.Error("Attempted to exit VR without a VR device loaded.");
FailedVRModeSwitch(false);
yield break;
}
//one frame after switch attempt
public void FailedVRModeSwitch(bool isVR)
{
if (_softVRSwitch) return;
//let tracked objects know a switch failed
VRModeSwitchTracker.FailVRModeSwitch(isVR);
}
//one frame before switch attempt
public void PreVRModeSwitch(bool isVR)
{
if (_softVRSwitch) return;
//let tracked objects know we are attempting to switch
VRModeSwitchTracker.PreVRModeSwitch(isVR);
}
//one frame after switch attempt
public void PostVRModeSwitch(bool isVR)
{
if (_softVRSwitch) return;
//close the menus
TryCatchHell.CloseCohtmlMenus();
//the base of VR checks
TryCatchHell.SetCheckVR(isVR);
TryCatchHell.SetMetaPort(isVR);
//game basics for functional gameplay post switch
TryCatchHell.RepositionCohtmlHud(isVR);
TryCatchHell.UpdateHudOperations(isVR);
TryCatchHell.DisableMirrorCanvas();
TryCatchHell.SwitchActiveCameraRigs(isVR);
TryCatchHell.ResetCVRInputManager();
TryCatchHell.UpdateRichPresence();
TryCatchHell.UpdateGestureReconizerCam();
TryCatchHell.UpdateMenuCoreData(isVR);
//let tracked objects know we switched
VRModeSwitchTracker.PostVRModeSwitch(isVR);
//reload avatar by default, optional for debugging
if (_reloadLocalAvatar)
{
TryCatchHell.ReloadLocalAvatar();
}
_switchInProgress = false;
}
}

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
</Reference>
<Reference Include="Aura2_Core">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Aura2_Core.dll</HintPath>
</Reference>
<Reference Include="cohtml.Net">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
</Reference>
<Reference Include="Cohtml.Runtime">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
</Reference>
<Reference Include="MelonLoader">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath>
</Reference>
<Reference Include="SteamVR">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\SteamVR.dll</HintPath>
</Reference>
<Reference Include="UIExpansionKit">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\UIExpansionKit.dll</HintPath>
</Reference>
<Reference Include="Unity.Postprocessing.Runtime">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XRModule">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="Deploy" AfterTargets="Build">
<Copy SourceFiles="$(TargetPath)" DestinationFolder="C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\" />
<Message Text="Copied $(TargetPath) to C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\" Importance="high" />
</Target>
</Project>

View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopVRSwitch", "DesktopVRSwitch.csproj", "{4008E6D1-32F9-449B-8820-80ACF0ED8233}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4008E6D1-32F9-449B-8820-80ACF0ED8233}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4008E6D1-32F9-449B-8820-80ACF0ED8233}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4008E6D1-32F9-449B-8820-80ACF0ED8233}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4008E6D1-32F9-449B-8820-80ACF0ED8233}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F6A1F88-B3D1-46F3-9370-9DDAE1F707C3}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,120 @@
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.IK.TrackingModules;
using ABI_RC.Systems.MovementSystem;
using HarmonyLib;
using NAK.Melons.DesktopVRSwitch.Patches;
using UnityEngine;
namespace NAK.Melons.DesktopVRSwitch.HarmonyPatches;
internal class PlayerSetupPatches
{
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerSetup), "Start")]
private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance)
{
if (CheckVR.Instance != null)
{
CheckVR.Instance.gameObject.AddComponent<DesktopVRSwitch>();
return;
}
__instance.gameObject.AddComponent<DesktopVRSwitch>();
DesktopVRSwitchMod.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>();
}
[HarmonyPostfix] //lazy fix so i dont need to wait few frames
[HarmonyPatch(typeof(TrackingPoint), "Initialize")]
private static void Postfix_TrackingPoint_Initialize(ref TrackingPoint __instance)
{
__instance.referenceTransform.localScale = Vector3.one;
}
[HarmonyPostfix] //lazy fix so device indecies can change properly
[HarmonyPatch(typeof(SteamVRTrackingModule), "ModuleDestroy")]
private static void Postfix_SteamVRTrackingModule_ModuleDestroy(ref SteamVRTrackingModule __instance)
{
for (int i = 0; i < __instance.TrackingPoints.Count; i++)
{
UnityEngine.Object.Destroy(__instance.TrackingPoints[i].referenceGameObject);
}
__instance.TrackingPoints.Clear();
}
}
internal class VRTrackerManagerPatches
{
[HarmonyPostfix]
[HarmonyPatch(typeof(VRTrackerManager), "Start")]
private static void Postfix_VRTrackerManager_Start(ref VRTrackerManager __instance)
{
__instance.gameObject.AddComponent<VRTrackerManagerTracker>();
}
}

54
DesktopVRSwitch/Main.cs Normal file
View file

@ -0,0 +1,54 @@
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 VRModeSwitchTracker 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.DesktopVRSwitch;
public class DesktopVRSwitchMod : MelonMod
{
internal const string SettingsCategory = "DesktopVRSwitch";
internal static MelonPreferences_Category mCategory;
internal static MelonLogger.Instance Logger;
internal static MelonPreferences_Entry<bool>
mSetting_EnterCalibrationOnSwitch;
public override void OnInitializeMelon()
{
Logger = LoggerInstance;
mCategory = MelonPreferences.CreateCategory(SettingsCategory);
mSetting_EnterCalibrationOnSwitch = mCategory.CreateEntry<bool>("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.");
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
ApplyPatches(typeof(HarmonyPatches.CVRPickupObjectPatches));
ApplyPatches(typeof(HarmonyPatches.CVRWorldPatches));
ApplyPatches(typeof(HarmonyPatches.CameraFacingObjectPatches));
ApplyPatches(typeof(HarmonyPatches.IKSystemPatches));
ApplyPatches(typeof(HarmonyPatches.MovementSystemPatches));
ApplyPatches(typeof(HarmonyPatches.VRTrackerManagerPatches));
}
private void ApplyPatches(Type type)
{
try
{
HarmonyInstance.PatchAll(type);
}
catch (Exception e)
{
Logger.Msg($"Failed while patching {type.Name}!");
Logger.Error(e);
}
}
}

View 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.DesktopVRSwitch.Patches;
public class CVRPickupObjectTracker : MonoBehaviour
{
public CVRPickupObject pickupObject;
public Transform storedGripOrigin;
void Start()
{
VRModeSwitchTracker.OnPostVRModeSwitch += PostVRModeSwitch;
}
void OnDestroy()
{
VRModeSwitchTracker.OnPostVRModeSwitch -= PostVRModeSwitch;
}
public void PostVRModeSwitch(bool isVR, Camera activeCamera)
{
if (pickupObject != null)
{
if (pickupObject._controllerRay != null) pickupObject._controllerRay.DropObject(true);
(storedGripOrigin, pickupObject.gripOrigin) = (pickupObject.gripOrigin, storedGripOrigin);
}
}
}

View file

@ -0,0 +1,24 @@
using ABI_RC.Core.Util.Object_Behaviour;
using UnityEngine;
namespace NAK.Melons.DesktopVRSwitch.Patches;
public class CameraFacingObjectTracker : MonoBehaviour
{
public CameraFacingObject cameraFacingObject;
void Start()
{
cameraFacingObject = GetComponent<CameraFacingObject>();
VRModeSwitchTracker.OnPostVRModeSwitch += PostVRModeSwitch;
}
void OnDestroy()
{
VRModeSwitchTracker.OnPostVRModeSwitch -= PostVRModeSwitch;
}
public void PostVRModeSwitch(bool isVR, Camera activeCamera)
{
cameraFacingObject.m_Camera = activeCamera;
}
}

View file

@ -0,0 +1,61 @@
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.TrackingModules;
using HarmonyLib;
using System.Reflection;
using UnityEngine;
namespace NAK.Melons.DesktopVRSwitch.Patches;
public class IKSystemTracker : MonoBehaviour
{
public IKSystem ikSystem;
public Traverse _traverseModules;
void Start()
{
ikSystem = GetComponent<IKSystem>();
_traverseModules = Traverse.Create(ikSystem).Field("_trackingModules");
VRModeSwitchTracker.OnPostVRModeSwitch += PostVRModeSwitch;
}
void OnDestroy()
{
VRModeSwitchTracker.OnPostVRModeSwitch -= PostVRModeSwitch;
}
public void PostVRModeSwitch(bool isVR, Camera activeCamera)
{
var _trackingModules = _traverseModules.GetValue<List<TrackingModule>>();
SteamVRTrackingModule openVRTrackingModule = _trackingModules.FirstOrDefault(m => m is SteamVRTrackingModule) as SteamVRTrackingModule;
if (openVRTrackingModule != null)
{
if (isVR)
{
openVRTrackingModule.ModuleStart();
}
else
{
//why named destroy when it doesnt ?
openVRTrackingModule.ModuleDestroy();
}
}
else
{
var steamVRTrackingModule = CreateSteamVRTrackingModule();
ikSystem.AddTrackingModule(steamVRTrackingModule);
}
//make it so you dont instantly end up in FBT from Desktop
IKSystem.firstAvatarLoaded = DesktopVRSwitchMod.mSetting_EnterCalibrationOnSwitch.Value;
//turn of finger tracking just in case user switched controllers
ikSystem.FingerSystem.controlActive = false;
}
//thanks for marking the constructor as internal
private SteamVRTrackingModule CreateSteamVRTrackingModule()
{
var steamVRTrackingModuleType = typeof(SteamVRTrackingModule);
var constructor = steamVRTrackingModuleType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
var instance = constructor.Invoke(null);
return (SteamVRTrackingModule)instance;
}
}

View file

@ -0,0 +1,48 @@
using ABI_RC.Systems.MovementSystem;
using UnityEngine;
namespace NAK.Melons.DesktopVRSwitch.Patches;
public class MovementSystemTracker : MonoBehaviour
{
public MovementSystem movementSystem;
public Vector3 preSwitchWorldPosition;
public Quaternion preSwitchWorldRotation;
void Start()
{
movementSystem = GetComponent<MovementSystem>();
VRModeSwitchTracker.OnPostVRModeSwitch += PreVRModeSwitch;
VRModeSwitchTracker.OnPostVRModeSwitch += PostVRModeSwitch;
}
void OnDestroy()
{
VRModeSwitchTracker.OnPostVRModeSwitch -= PreVRModeSwitch;
VRModeSwitchTracker.OnPostVRModeSwitch -= PostVRModeSwitch;
}
public void PreVRModeSwitch(bool isVR, 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).
}
public void PostVRModeSwitch(bool isVR, 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 (!isVR) movementSystem.UpdateColliderCenter(movementSystem.transform.position);
}
}

View 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.DesktopVRSwitch.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)
{
DesktopVRSwitchMod.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>());
}
}
}

View file

@ -0,0 +1,45 @@
using ABI_RC.Core.Player;
using UnityEngine;
using UnityEngine.Events;
namespace NAK.Melons.DesktopVRSwitch.Patches;
public class VRModeSwitchTracker
{
public static event UnityAction<bool, Camera> OnPreVRModeSwitch;
public static event UnityAction<bool, Camera> OnPostVRModeSwitch;
public static event UnityAction<bool, Camera> OnFailVRModeSwitch;
public static void PreVRModeSwitch(bool isVR)
{
TryCatchHell.TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Invoking VRModeSwitchTracker.OnPreVRModeSwitch.");
Camera activeCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
VRModeSwitchTracker.OnPreVRModeSwitch?.Invoke(isVR, activeCamera);
},
"Error while invoking VRModeSwitchTracker.OnPreVRModeSwitch. Did someone do a fucky?");
}
public static void PostVRModeSwitch(bool isVR)
{
TryCatchHell.TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Invoking VRModeSwitchTracker.OnPostVRModeSwitch.");
Camera activeCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
VRModeSwitchTracker.OnPostVRModeSwitch?.Invoke(isVR, activeCamera);
},
"Error while invoking VRModeSwitchTracker.OnPostVRModeSwitch. Did someone do a fucky?");
}
public static void FailVRModeSwitch(bool isVR)
{
TryCatchHell.TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Invoking VRModeSwitchTracker.OnFailVRModeSwitch.");
Camera activeCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
VRModeSwitchTracker.OnFailVRModeSwitch?.Invoke(isVR, activeCamera);
},
"Error while invoking OnFailVRModeSwitch.OnPreVRModeSwitch. Did someone do a fucky?");
}
}

View file

@ -0,0 +1,35 @@
using ABI_RC.Core.Player;
using HarmonyLib;
using UnityEngine;
namespace NAK.Melons.DesktopVRSwitch.Patches;
public class VRTrackerManagerTracker : MonoBehaviour
{
public VRTrackerManager vrTrackerManager;
public Traverse _hasCheckedForKnucklesTraverse;
public Traverse _posesTraverse;
void Start()
{
vrTrackerManager = GetComponent<VRTrackerManager>();
_posesTraverse = Traverse.Create(vrTrackerManager).Field("poses");
_hasCheckedForKnucklesTraverse = Traverse.Create(vrTrackerManager).Field("hasCheckedForKnuckles");
VRModeSwitchTracker.OnPostVRModeSwitch += PostVRModeSwitch;
}
void OnDestroy()
{
VRModeSwitchTracker.OnPostVRModeSwitch -= PostVRModeSwitch;
}
public void PostVRModeSwitch(bool isVR, Camera activeCamera)
{
//force the VRTrackerManager to reset anything its stored
//this makes it get correct Left/Right hand if entering VR with different controllers
//or if you restarted SteamVR and controllers are now in swapped index
vrTrackerManager.leftHand = null;
vrTrackerManager.rightHand = null;
_posesTraverse.SetValue(null);
_hasCheckedForKnucklesTraverse.SetValue(false);
}
}

View file

@ -0,0 +1,31 @@
using MelonLoader;
using NAK.Melons.DesktopVRSwitch.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.Melons.DesktopVRSwitch))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.Melons.DesktopVRSwitch))]
[assembly: MelonInfo(
typeof(NAK.Melons.DesktopVRSwitch.DesktopVRSwitchMod),
nameof(NAK.Melons.DesktopVRSwitch),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidOnSteam/DesktopVRSwitch"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(ConsoleColor.DarkCyan)]
[assembly: HarmonyDontPatchAll]
namespace NAK.Melons.DesktopVRSwitch.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "4.3.5";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,189 @@
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 HarmonyLib;
using UnityEngine;
namespace NAK.Melons.DesktopVRSwitch;
internal class TryCatchHell
{
internal static void TryCatchWrapper(Action action, string errorMsg, params object[] msgArgs)
{
try
{
action();
}
catch (Exception ex)
{
DesktopVRSwitchMod.Logger.Error(string.Format(errorMsg, msgArgs));
DesktopVRSwitchMod.Logger.Msg(ex.Message);
}
}
internal static void CloseCohtmlMenus()
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.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 isVR)
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg($"Setting CheckVR hasVrDeviceLoaded to {isVR}.");
CheckVR.Instance.hasVrDeviceLoaded = isVR;
},
"Setting CheckVR hasVrDeviceLoaded failed.");
}
internal static void SetMetaPort(bool isVR)
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg($"Setting MetaPort isUsingVr to {isVR}.");
MetaPort.Instance.isUsingVr = isVR;
},
"Setting MetaPort isUsingVr failed.");
}
internal static void RepositionCohtmlHud(bool isVR)
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Configuring new hud affinity for CohtmlHud.");
CohtmlHud.Instance.gameObject.transform.parent = isVR ? 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 isVR)
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Switching HudOperations worldLoadingItem & worldLoadStatus.");
HudOperations.Instance.worldLoadingItem = isVR ? HudOperations.Instance.worldLoadingItemVr : HudOperations.Instance.worldLoadingItemDesktop;
HudOperations.Instance.worldLoadStatus = isVR ? HudOperations.Instance.worldLoadStatusVr : HudOperations.Instance.worldLoadStatusDesktop;
},
"Failed switching HudOperations objects.");
}
internal static void DisableMirrorCanvas()
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.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 isVR)
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Switching active PlayerSetup camera rigs. Updating Desktop camera FOV.");
PlayerSetup.Instance.desktopCameraRig.SetActive(!isVR);
PlayerSetup.Instance.vrCameraRig.SetActive(isVR);
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(() =>
{
DesktopVRSwitchMod.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 ResetCVRInputManager()
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.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;
},
"Failed to reset CVRInputManager inputs.");
}
internal static void ReloadLocalAvatar()
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.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))
{
DesktopVRSwitchMod.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))
{
DesktopVRSwitchMod.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(() =>
{
DesktopVRSwitchMod.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.");
}
internal static void UpdateMenuCoreData(bool isVR)
{
TryCatchWrapper(() =>
{
DesktopVRSwitchMod.Logger.Msg("Updating CVR_Menu_Data core data.");
CVR_MenuManager.Instance.coreData.core.inVr = isVR;
},
"Failed to update CVR_Menu_Data core data.");
}
}

View file

@ -0,0 +1,23 @@
{
"_id": 103,
"name": "DesktopVRSwitch",
"modversion": "4.3.5",
"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.5/DesktopVRSwitch.dll",
"sourcelink": "https://github.com/NotAKidOnSteam/DesktopVRSwitch/",
"changelog": "- Reinitialize SteamVR input on switch. Correct fixedDeltaTime when entering Desktop.\n- Tweak to initial switch position for correcting offsets.\n- Fixed FBT tracking point initial size.\n- Update core menu data so QM Recalibrate/SeatedPlay button works.",
"embedcolor": "3498db"
}

View file

@ -13,23 +13,8 @@ AAS will also not be updated unless the expected data matches what is received.
The avatar will stay in the default animator state until AAS data is received that is deemed correct.
You will no longer sync garbage AAS while switching avatar.
# CVRGizmos
Adds in-game gizmos to CCK components.
Current implementation may be a bit ugly, but at least it doesn't tank FPS.
Uses modified version of Popcron.Gizmos:
https://github.com/popcron/gizmos
![ChilloutVR_vQAWKRkt73](https://user-images.githubusercontent.com/37721153/190173732-368dec7a-d56e-47a0-bc38-3c7f38caa0bc.png)
# ClearHudNotifications
Simple mod to clear hud notifications when joining an online instance. Can also manually clear notifications by pressing F4.
There is no native method to clear notifications, so I force an immediate notification to clear the buffer.
---
# Blackout
Functionality heavily inspired by VRSleeper on Booth: https://booth.pm/ja/items/2151940
@ -64,7 +49,27 @@ There is no native method to clear notifications, so I force an immediate notifi
* Awake Threshold - Degrees of movement to return full vision.
* Enter Drowsy Time - How many minutes without movement until enter drowsy mode.
* Enter Sleep Time - How many seconds without movement until enter sleep mode.
---
# ClearHudNotifications
Simple mod to clear hud notifications when joining an online instance. Can also manually clear notifications by pressing F4.
There is no native method to clear notifications, so I force an immediate notification to clear the buffer.
---
# CVRGizmos
Adds in-game gizmos to CCK components.
Current implementation may be a bit ugly, but at least it doesn't tank FPS.
Uses modified version of Popcron.Gizmos:
https://github.com/popcron/gizmos
![ChilloutVR_vQAWKRkt73](https://user-images.githubusercontent.com/37721153/190173732-368dec7a-d56e-47a0-bc38-3c7f38caa0bc.png)
---
# DesktopVRIK
Adds VRIK to Desktop ChilloutVR avatars. No longer will you be a liveless sliding statue~!
@ -85,6 +90,27 @@ https://user-images.githubusercontent.com/37721153/221870123-fbe4f5e8-8d6e-4a43-
https://feedback.abinteractive.net/p/desktop-feet-ik-for-avatars
https://feedback.abinteractive.net/p/pivot-desktop-camera-with-head
---
# DesktopVRSwitch
Allows you to switch between Desktop and VR with a keybind.
Press Control + F6 to switch. SteamVR will automatically start if it isn't already running.
---
Almost all base game features & systems that differ when in VR are now updated after switch. There are still very likely small quirks that need ironing out still, but everything major is now fixed and accounted for.
This mod will likely cause issues with other mods that are not built for or expect VRMode changes during runtime.
## There are two versions of this mod!
**DesktopVRSwitch** is built for the Stable branch of ChilloutVR. (OpenVR)
**DesktopXRSwitch** is built for the Experimental branch of ChilloutVR (OpenXR)
Once the Experimental branch of ChilloutVR hits Stable, the mod name will be changing from VR -> XR.
---
# FuckMetrics
This mod limits UpdateMetrics & SendCoreUpdate while the menus are closed. This helps to alleviate hitching and performance issues, particularly with FPS drops while unmuted in online instances and VRIK tapping in place.
@ -255,7 +281,7 @@ https://user-images.githubusercontent.com/37721153/231351589-07f794f3-f542-4cb4-
## Relevant Feedback Posts:
https://feedback.abinteractive.net/p/z-for-undo-in-game
---
---
Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games