mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +00:00
LegacyContentMitigation: initial commit
manual "multipass" in legacy worlds
This commit is contained in:
parent
3d6b1bbd59
commit
94515efbe3
8 changed files with 645 additions and 0 deletions
69
LegacyContentMitigation/Components/CameraCallbackLogger.cs
Normal file
69
LegacyContentMitigation/Components/CameraCallbackLogger.cs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Text;
|
||||||
|
using MelonLoader;
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation.Debug;
|
||||||
|
|
||||||
|
public class CameraCallbackLogger
|
||||||
|
{
|
||||||
|
private static CameraCallbackLogger instance;
|
||||||
|
private readonly List<string> frameCallbacks = new();
|
||||||
|
private bool isListening;
|
||||||
|
private readonly StringBuilder logBuilder = new();
|
||||||
|
|
||||||
|
public static CameraCallbackLogger Instance => instance ??= new CameraCallbackLogger();
|
||||||
|
|
||||||
|
private void RegisterCallbacks()
|
||||||
|
{
|
||||||
|
Camera.onPreCull += (cam) => LogCallback(cam, "OnPreCull");
|
||||||
|
Camera.onPreRender += (cam) => LogCallback(cam, "OnPreRender");
|
||||||
|
Camera.onPostRender += (cam) => LogCallback(cam, "OnPostRender");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterCallbacks()
|
||||||
|
{
|
||||||
|
Camera.onPreCull -= (cam) => LogCallback(cam, "OnPreCull");
|
||||||
|
Camera.onPreRender -= (cam) => LogCallback(cam, "OnPreRender");
|
||||||
|
Camera.onPostRender -= (cam) => LogCallback(cam, "OnPostRender");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogCameraEvents()
|
||||||
|
{
|
||||||
|
MelonCoroutines.Start(LoggingCoroutine());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator LoggingCoroutine()
|
||||||
|
{
|
||||||
|
yield return null; // idk at what point in frame start occurs
|
||||||
|
|
||||||
|
// First frame: Register and listen
|
||||||
|
RegisterCallbacks();
|
||||||
|
isListening = true;
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
// Second frame: Log and cleanup
|
||||||
|
isListening = false;
|
||||||
|
PrintFrameLog();
|
||||||
|
UnregisterCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogCallback(Camera camera, string callbackName)
|
||||||
|
{
|
||||||
|
if (!isListening) return;
|
||||||
|
frameCallbacks.Add($"{camera.name} - {callbackName} (Depth: {camera.depth})");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintFrameLog()
|
||||||
|
{
|
||||||
|
logBuilder.Clear();
|
||||||
|
logBuilder.AppendLine("\nCamera Callbacks for Frame:");
|
||||||
|
|
||||||
|
foreach (var callback in frameCallbacks)
|
||||||
|
logBuilder.AppendLine(callback);
|
||||||
|
|
||||||
|
LegacyContentMitigationMod.Logger.Msg(logBuilder.ToString());
|
||||||
|
|
||||||
|
frameCallbacks.Clear();
|
||||||
|
}
|
||||||
|
}
|
228
LegacyContentMitigation/Components/FakeMultiPassHack.cs
Normal file
228
LegacyContentMitigation/Components/FakeMultiPassHack.cs
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
using ABI_RC.Core;
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Systems.UI;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
using UnityEngine.XR;
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation;
|
||||||
|
|
||||||
|
public class FakeMultiPassHack : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static readonly int s_WorldSpaceCameraPos = Shader.PropertyToID("_WorldSpaceCameraPos");
|
||||||
|
|
||||||
|
public static Action<bool> OnMultiPassActiveChanged;
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public static FakeMultiPassHack Instance { get; set; }
|
||||||
|
public bool IsActive => IsEnabled && isActiveAndEnabled;
|
||||||
|
public bool IsEnabled { get; private set; }
|
||||||
|
public Camera.MonoOrStereoscopicEye RenderingEye { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Fields
|
||||||
|
|
||||||
|
private Camera _mainCamera;
|
||||||
|
private Camera _leftEye;
|
||||||
|
private Camera _rightEye;
|
||||||
|
|
||||||
|
private GameObject _leftEyeObject;
|
||||||
|
private GameObject _rightEyeObject;
|
||||||
|
|
||||||
|
private RenderTexture _leftTexture;
|
||||||
|
private RenderTexture _rightTexture;
|
||||||
|
|
||||||
|
private CommandBuffer _shaderGlobalBuffer;
|
||||||
|
private CommandBuffer _leftEyeBuffer;
|
||||||
|
|
||||||
|
private int CachedCullingMask;
|
||||||
|
private bool _isInitialized;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unity Lifecycle
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
Camera.onPreRender += OnPreRenderCallback;
|
||||||
|
if (IsEnabled) _mainCamera.cullingMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
Camera.onPreRender -= OnPreRenderCallback;
|
||||||
|
if (IsEnabled) _mainCamera.cullingMask = CachedCullingMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
if (_leftEye != null) RemoveCameraFromWorldTransitionSystem(_leftEye);
|
||||||
|
if (_rightEye != null) RemoveCameraFromWorldTransitionSystem(_rightEye);
|
||||||
|
|
||||||
|
if (_leftTexture != null) _leftTexture.Release();
|
||||||
|
if (_rightTexture != null) _rightTexture.Release();
|
||||||
|
_shaderGlobalBuffer?.Release();
|
||||||
|
_leftEyeBuffer?.Release();
|
||||||
|
|
||||||
|
if (_leftEyeObject != null) Destroy(_leftEyeObject);
|
||||||
|
if (_rightEyeObject != null) Destroy(_rightEyeObject);
|
||||||
|
|
||||||
|
return;
|
||||||
|
void RemoveCameraFromWorldTransitionSystem(Camera cam)
|
||||||
|
{
|
||||||
|
if (cam.TryGetComponent(out WorldTransitionCamera effectCam)) Destroy(effectCam);
|
||||||
|
WorldTransitionSystem.Cameras.Remove(cam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
public void SetMultiPassActive(bool active)
|
||||||
|
{
|
||||||
|
if (active == IsEnabled) return;
|
||||||
|
IsEnabled = active;
|
||||||
|
|
||||||
|
if (active && !_isInitialized) DoInitialSetup();
|
||||||
|
|
||||||
|
_mainCamera.cullingMask = IsActive ? 0 : CachedCullingMask;
|
||||||
|
|
||||||
|
OnMultiPassActiveChanged?.Invoke(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMainCameraChanged()
|
||||||
|
{
|
||||||
|
if (!_isInitialized) return;
|
||||||
|
|
||||||
|
CachedCullingMask = _mainCamera.cullingMask;
|
||||||
|
if (IsActive) _mainCamera.cullingMask = 0;
|
||||||
|
|
||||||
|
CVRTools.CopyToDestCam(_mainCamera, _leftEye);
|
||||||
|
CVRTools.CopyToDestCam(_mainCamera, _rightEye);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
|
||||||
|
private void DoInitialSetup()
|
||||||
|
{
|
||||||
|
_mainCamera = GetComponent<Camera>();
|
||||||
|
CachedCullingMask = _mainCamera.cullingMask;
|
||||||
|
|
||||||
|
_shaderGlobalBuffer = new CommandBuffer();
|
||||||
|
_leftEyeBuffer = new CommandBuffer();
|
||||||
|
|
||||||
|
SetupEye("Left Eye", out _leftEyeObject, out _leftEye, _leftEyeBuffer);
|
||||||
|
SetupEye("Right Eye", out _rightEyeObject, out _rightEye, null);
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
void SetupEye(string camName, out GameObject eyeObj, out Camera eye, CommandBuffer eyeBuffer)
|
||||||
|
{
|
||||||
|
eyeObj = new GameObject(camName);
|
||||||
|
eyeObj.transform.parent = transform;
|
||||||
|
eyeObj.transform.localScale = Vector3.one;
|
||||||
|
eye = eyeObj.AddComponent<Camera>();
|
||||||
|
eye.enabled = false;
|
||||||
|
|
||||||
|
// Correct camera world space pos (nameplate shader)
|
||||||
|
eye.AddCommandBuffer(CameraEvent.BeforeDepthTexture, _shaderGlobalBuffer);
|
||||||
|
eye.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, _shaderGlobalBuffer);
|
||||||
|
eye.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _shaderGlobalBuffer);
|
||||||
|
|
||||||
|
// normalizedViewport parameter is ignored, so we cannot draw mesh on right eye :)
|
||||||
|
if (eyeBuffer != null)
|
||||||
|
{
|
||||||
|
eye.AddCommandBuffer(CameraEvent.BeforeDepthTexture, eyeBuffer);
|
||||||
|
eye.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, eyeBuffer);
|
||||||
|
eye.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, eyeBuffer);
|
||||||
|
// notice how we pass fucked param vs UnityEngine.Rendering.XRUtils
|
||||||
|
eyeBuffer.DrawOcclusionMesh(new RectInt(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldTransitionSystem.AddCamera(eye);
|
||||||
|
CVRTools.CopyToDestCam(_mainCamera, eye);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Rendering
|
||||||
|
|
||||||
|
private void OnPreRenderCallback(Camera cam)
|
||||||
|
{
|
||||||
|
if (!IsEnabled || !_isInitialized) return;
|
||||||
|
|
||||||
|
if (cam.CompareTag("MainCamera"))
|
||||||
|
{
|
||||||
|
EnsureRenderTexturesCreated();
|
||||||
|
RenderEyePair();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureRenderTexturesCreated()
|
||||||
|
{
|
||||||
|
int eyeWidth = XRSettings.eyeTextureWidth;
|
||||||
|
int eyeHeight = XRSettings.eyeTextureHeight;
|
||||||
|
|
||||||
|
bool needsUpdate = _leftTexture == null || _rightTexture == null ||
|
||||||
|
_leftTexture.width != eyeWidth || _leftTexture.height != eyeHeight;
|
||||||
|
|
||||||
|
if (!needsUpdate) return;
|
||||||
|
|
||||||
|
if (_leftTexture != null) _leftTexture.Release();
|
||||||
|
if (_rightTexture != null) _rightTexture.Release();
|
||||||
|
|
||||||
|
_leftTexture = new RenderTexture(eyeWidth, eyeHeight, 24, RenderTextureFormat.ARGBHalf);
|
||||||
|
_rightTexture = new RenderTexture(eyeWidth, eyeHeight, 24, RenderTextureFormat.ARGBHalf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderEyePair()
|
||||||
|
{
|
||||||
|
_shaderGlobalBuffer.Clear();
|
||||||
|
_shaderGlobalBuffer.SetGlobalVector(s_WorldSpaceCameraPos, _mainCamera.transform.position);
|
||||||
|
|
||||||
|
Camera realVRCamera = PlayerSetup.Instance.vrCam;
|
||||||
|
|
||||||
|
RenderingEye = Camera.MonoOrStereoscopicEye.Left;
|
||||||
|
PlayerSetup.Instance.vrCam = _leftEye; // so we trigger head hiding
|
||||||
|
RenderEye(_leftEye, _leftTexture, Camera.StereoscopicEye.Left);
|
||||||
|
|
||||||
|
RenderingEye = Camera.MonoOrStereoscopicEye.Right;
|
||||||
|
PlayerSetup.Instance.vrCam = _rightEye; // so we trigger head hiding
|
||||||
|
RenderEye(_rightEye, _rightTexture, Camera.StereoscopicEye.Right);
|
||||||
|
|
||||||
|
RenderingEye = Camera.MonoOrStereoscopicEye.Mono; // bleh
|
||||||
|
PlayerSetup.Instance.vrCam = realVRCamera; // reset back to real cam
|
||||||
|
|
||||||
|
return;
|
||||||
|
void RenderEye(Camera eyeCamera, RenderTexture targetTexture, Camera.StereoscopicEye eye)
|
||||||
|
{
|
||||||
|
eyeCamera.CopyFrom(_mainCamera);
|
||||||
|
eyeCamera.targetTexture = targetTexture;
|
||||||
|
eyeCamera.cullingMask = CachedCullingMask;
|
||||||
|
eyeCamera.stereoTargetEye = StereoTargetEyeMask.None;
|
||||||
|
eyeCamera.projectionMatrix = _mainCamera.GetStereoProjectionMatrix(eye);
|
||||||
|
eyeCamera.worldToCameraMatrix = _mainCamera.GetStereoViewMatrix(eye);
|
||||||
|
eyeCamera.Render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRenderImage(RenderTexture source, RenderTexture destination)
|
||||||
|
{
|
||||||
|
if (!IsEnabled || !_isInitialized || _leftTexture == null || _rightTexture == null)
|
||||||
|
{
|
||||||
|
Graphics.Blit(source, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics.CopyTexture(_leftTexture, 0, destination, 0);
|
||||||
|
Graphics.CopyTexture(_rightTexture, 0, destination, 1);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
86
LegacyContentMitigation/Integrations/BtkUiAddon.cs
Normal file
86
LegacyContentMitigation/Integrations/BtkUiAddon.cs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
using ABI_RC.Core.InteractionSystem;
|
||||||
|
using ABI_RC.Core.Networking.API.Responses;
|
||||||
|
using ABI.CCK.Components;
|
||||||
|
using BTKUILib;
|
||||||
|
using BTKUILib.UIObjects;
|
||||||
|
using BTKUILib.UIObjects.Components;
|
||||||
|
using NAK.LegacyContentMitigation.Debug;
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation.Integrations;
|
||||||
|
|
||||||
|
public static class BtkUiAddon
|
||||||
|
{
|
||||||
|
private static ToggleButton _currentModState;
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
// Create menu late to ensure we at bottom.
|
||||||
|
// Doing this cause these settings are "Advanced" & mostly for debugging.
|
||||||
|
QuickMenuAPI.OnMenuGenerated += SetupCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupCategory(CVR_MenuManager _)
|
||||||
|
{
|
||||||
|
QuickMenuAPI.OnMenuGenerated -= SetupCategory;
|
||||||
|
|
||||||
|
Category category = QuickMenuAPI.MiscTabPage.AddCategory(ModSettings.LCM_SettingsCategory, ModSettings.ModName, true, true, true);
|
||||||
|
|
||||||
|
ToggleButton autoButton = category.AddMelonToggle(ModSettings.EntryAutoForLegacyWorlds);
|
||||||
|
autoButton.OnValueUpdated += (_) =>
|
||||||
|
{
|
||||||
|
if (CVRWorld.CompatibilityVersion == CompatibilityVersions.NotSpi)
|
||||||
|
QuickMenuAPI.ShowNotice("Legacy World Notice",
|
||||||
|
"You must reload this World Bundle for Shader Replacement to be undone / applied. " +
|
||||||
|
"Load into a different world and then rejoin.");
|
||||||
|
};
|
||||||
|
|
||||||
|
_currentModState = category.AddToggle(string.Empty, string.Empty, false);
|
||||||
|
_currentModState.OnValueUpdated += OnCurrentStateToggled;
|
||||||
|
|
||||||
|
Button printCameraCallbacksButton = category.AddButton("DEBUG LOG CAMERAS",
|
||||||
|
string.Empty, "Records Camera events & logs them next frame. Useful for determining camera render order shenanigans.");
|
||||||
|
printCameraCallbacksButton.OnPress += () => CameraCallbackLogger.Instance.LogCameraEvents();
|
||||||
|
|
||||||
|
OnCurrentStateToggled(FakeMultiPassHack.Instance.IsEnabled);
|
||||||
|
FakeMultiPassHack.OnMultiPassActiveChanged += OnMultiPassActiveChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnCurrentStateToggled(bool state)
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
_currentModState.ToggleValue = false; // dont visually update
|
||||||
|
QuickMenuAPI.ShowConfirm("Legacy Mitigation Warning",
|
||||||
|
"This will change how the main VR view is rendered and cause a noticeable performance hit. " +
|
||||||
|
"It is recommended to only enable this within Worlds that require it (Legacy Content/Broken Shaders). " +
|
||||||
|
"Shader Replacement will not occur for ALL content that is loaded while enabled. " +
|
||||||
|
"If this World is Legacy and already Shader Replaced, you must enable Auto For Legacy Worlds instead, " +
|
||||||
|
"load a different World, and then join back.",
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
FakeMultiPassHack.Instance.SetMultiPassActive(true);
|
||||||
|
OnMultiPassActiveChanged(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FakeMultiPassHack.Instance.SetMultiPassActive(false);
|
||||||
|
OnMultiPassActiveChanged(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnMultiPassActiveChanged(bool state)
|
||||||
|
{
|
||||||
|
_currentModState.ToggleValue = state;
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
_currentModState.ToggleName = "Currently Active";
|
||||||
|
_currentModState.ToggleTooltip = "Fake Multi Pass is currently active.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_currentModState.ToggleName = "Currently Inactive";
|
||||||
|
_currentModState.ToggleTooltip = "Fake Multi Pass is inactive.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
LegacyContentMitigation/LegacyContentMitigation.csproj
Normal file
14
LegacyContentMitigation/LegacyContentMitigation.csproj
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="BTKUILib">
|
||||||
|
<HintPath>..\.ManagedLibs\BTKUILib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="TheClapper">
|
||||||
|
<HintPath>..\.ManagedLibs\TheClapper.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
67
LegacyContentMitigation/Main.cs
Normal file
67
LegacyContentMitigation/Main.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using ABI_RC.Core.InteractionSystem;
|
||||||
|
using MelonLoader;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation;
|
||||||
|
|
||||||
|
public class LegacyContentMitigationMod : MelonMod
|
||||||
|
{
|
||||||
|
internal static MelonLogger.Instance Logger;
|
||||||
|
|
||||||
|
#region Melon Preferences
|
||||||
|
|
||||||
|
// private static readonly MelonPreferences_Category Category =
|
||||||
|
// MelonPreferences.CreateCategory(nameof(LegacyContentMitigationMod));
|
||||||
|
//
|
||||||
|
// private static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
||||||
|
// Category.CreateEntry(
|
||||||
|
// "use_legacy_mitigation",
|
||||||
|
// true,
|
||||||
|
// "Enabled",
|
||||||
|
// description: "Enable legacy content camera hack when in Legacy worlds.");
|
||||||
|
|
||||||
|
#endregion Melon Preferences
|
||||||
|
|
||||||
|
#region Melon Events
|
||||||
|
|
||||||
|
public override void OnInitializeMelon()
|
||||||
|
{
|
||||||
|
Logger = LoggerInstance;
|
||||||
|
|
||||||
|
ApplyPatches(typeof(Patches.PlayerSetup_Patches)); // add MultiPassCamera to VR camera
|
||||||
|
ApplyPatches(typeof(Patches.SceneLoaded_Patches)); // enable / disable in legacy worlds
|
||||||
|
ApplyPatches(typeof(Patches.CVRWorld_Patches)); // post processing shit
|
||||||
|
ApplyPatches(typeof(Patches.CVRTools_Patches)); // prevent shader replacement when fix is active
|
||||||
|
ApplyPatches(typeof(Patches.HeadHiderManager_Patches)); // prevent main cam triggering early head hide
|
||||||
|
|
||||||
|
InitializeIntegration("BTKUILib", Integrations.BtkUiAddon.Initialize); // quick menu options
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Melon Events
|
||||||
|
|
||||||
|
#region Melon Mod Utilities
|
||||||
|
|
||||||
|
private static void InitializeIntegration(string modName, Action integrationAction)
|
||||||
|
{
|
||||||
|
if (RegisteredMelons.All(it => it.Info.Name != modName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Logger.Msg($"Initializing {modName} integration.");
|
||||||
|
integrationAction.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyPatches(Type type)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HarmonyInstance.PatchAll(type);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LoggerInstance.Msg($"Failed while patching {type.Name}!");
|
||||||
|
LoggerInstance.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Melon Mod Utilities
|
||||||
|
}
|
24
LegacyContentMitigation/ModSettings.cs
Normal file
24
LegacyContentMitigation/ModSettings.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using MelonLoader;
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation;
|
||||||
|
|
||||||
|
internal static class ModSettings
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
internal const string ModName = nameof(LegacyContentMitigation);
|
||||||
|
internal const string LCM_SettingsCategory = "Legacy Content Mitigation";
|
||||||
|
|
||||||
|
#endregion Constants
|
||||||
|
|
||||||
|
#region Melon Preferences
|
||||||
|
|
||||||
|
private static readonly MelonPreferences_Category Category =
|
||||||
|
MelonPreferences.CreateCategory(ModName);
|
||||||
|
|
||||||
|
internal static readonly MelonPreferences_Entry<bool> EntryAutoForLegacyWorlds =
|
||||||
|
Category.CreateEntry("auto_for_legacy_worlds", true,
|
||||||
|
"Auto For Legacy Worlds", description: "Should Legacy View be auto enabled for detected Legacy worlds?");
|
||||||
|
|
||||||
|
#endregion Melon Preferences
|
||||||
|
}
|
125
LegacyContentMitigation/Patches.cs
Normal file
125
LegacyContentMitigation/Patches.cs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
using ABI_RC.Core;
|
||||||
|
using ABI_RC.Core.Base;
|
||||||
|
using ABI_RC.Core.Base.Jobs;
|
||||||
|
using ABI_RC.Core.Networking.API.Responses;
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Core.Player.LocalClone;
|
||||||
|
using ABI_RC.Core.Player.TransformHider;
|
||||||
|
using ABI.CCK.Components;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering.PostProcessing;
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation.Patches;
|
||||||
|
|
||||||
|
internal static class PlayerSetup_Patches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))]
|
||||||
|
private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance)
|
||||||
|
{
|
||||||
|
FakeMultiPassHack.Instance = __instance.vrCam.AddComponentIfMissing<FakeMultiPassHack>();
|
||||||
|
FakeMultiPassHack.Instance.enabled = ModSettings.EntryAutoForLegacyWorlds.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class SceneLoaded_Patches
|
||||||
|
{
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(SceneLoaded), nameof(SceneLoaded.OnSceneLoadedHandleJob))]
|
||||||
|
private static void Prefix_SceneLoaded_OnSceneLoadedHandleJob()
|
||||||
|
{
|
||||||
|
if (!ModSettings.EntryAutoForLegacyWorlds.Value)
|
||||||
|
{
|
||||||
|
LegacyContentMitigationMod.Logger.Msg("LegacyContentMitigationMod is disabled.");
|
||||||
|
FakeMultiPassHack.Instance.SetMultiPassActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sceneIsNotSpi = CVRWorld.CompatibilityVersion == CompatibilityVersions.NotSpi;
|
||||||
|
string logText = sceneIsNotSpi
|
||||||
|
? "Legacy world detected, enabling legacy content mitigation."
|
||||||
|
: "Loaded scene is not considered Legacy content. Disabling if active.";
|
||||||
|
|
||||||
|
LegacyContentMitigationMod.Logger.Msg(logText);
|
||||||
|
FakeMultiPassHack.Instance.SetMultiPassActive(sceneIsNotSpi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class CVRWorld_Patches
|
||||||
|
{
|
||||||
|
// Third Person patches same methods:
|
||||||
|
// https://github.com/NotAKidoS/NAK_CVR_Mods/blob/3d6b1bbd59d23be19fe3594e104ad26e4ac0adcd/ThirdPerson/Patches.cs#L15-L22
|
||||||
|
[HarmonyPriority(Priority.Last)]
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(CVRWorld), nameof(CVRWorld.CopyRefCamValues))]
|
||||||
|
[HarmonyPatch(typeof(CVRWorld), nameof(CVRWorld.SetDefaultCamValues))]
|
||||||
|
private static void Postfix_CVRWorld_SetDefaultCamValues(ref CVRWorld __instance)
|
||||||
|
{
|
||||||
|
LegacyContentMitigationMod.Logger.Msg("Legacy world camera values updated.");
|
||||||
|
FakeMultiPassHack.Instance.OnMainCameraChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(CVRWorld), nameof(CVRWorld.UpdatePostProcessing))]
|
||||||
|
private static void Postfix_CVRWorld_UpdatePostProcessing(ref CVRWorld __instance)
|
||||||
|
{
|
||||||
|
if (!FakeMultiPassHack.Instance.IsActive) return;
|
||||||
|
foreach (PostProcessEffectSettings motionBlur in __instance._postProcessingMotionBlurList)
|
||||||
|
motionBlur.active = false; // force off cause its fucked and no one cares
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class CVRTools_Patches
|
||||||
|
{
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(CVRTools), nameof(CVRTools.ReplaceShaders), typeof(Material), typeof(string))]
|
||||||
|
private static bool Prefix_CVRTools_ReplaceShaders(Material material, string fallbackShaderName = "")
|
||||||
|
{
|
||||||
|
// When in a legacy world with the hack enabled, do not replace shaders
|
||||||
|
return !FakeMultiPassHack.Instance.IsActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class HeadHiderManager_Patches
|
||||||
|
{
|
||||||
|
// despite the visual clone not being normally accessible, i fix it cause mod:
|
||||||
|
// https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/VisualCloneFix
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(TransformHiderManager), nameof(TransformHiderManager.OnPreRenderCallback))]
|
||||||
|
[HarmonyPatch(typeof(TransformHiderManager), nameof(TransformHiderManager.OnPreRenderCallback))]
|
||||||
|
[HarmonyPatch(typeof(LocalCloneManager), nameof(LocalCloneManager.OnPreRenderCallback))]
|
||||||
|
[HarmonyPatch(typeof(LocalCloneManager), nameof(LocalCloneManager.OnPostRenderCallback))]
|
||||||
|
private static bool Prefix_HeadHiderManagers_OnRenderCallbacks(Camera cam)
|
||||||
|
{
|
||||||
|
if (!FakeMultiPassHack.Instance.IsActive)
|
||||||
|
return true; // not active, no need
|
||||||
|
|
||||||
|
// dont let real camera trigger head hiding to occur or reset- leave it to the left/right eyes
|
||||||
|
return !cam.CompareTag("MainCamera"); // we spoof playersetup.activeCam, need check tag
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(TransformHiderManager), nameof(TransformHiderManager.OnPostRenderCallback))]
|
||||||
|
[HarmonyPatch(typeof(LocalCloneManager), nameof(LocalCloneManager.OnPostRenderCallback))]
|
||||||
|
private static void Prefix_HeadHiderManagers_OnPostRenderCallback(Camera cam, ref MonoBehaviour __instance)
|
||||||
|
{
|
||||||
|
if (!FakeMultiPassHack.Instance.IsActive) return;
|
||||||
|
|
||||||
|
if (FakeMultiPassHack.Instance.RenderingEye == Camera.MonoOrStereoscopicEye.Left)
|
||||||
|
SetResetAfterRenderFlag(__instance, true); // so right eye mirror sees head
|
||||||
|
|
||||||
|
if (FakeMultiPassHack.Instance.RenderingEye == Camera.MonoOrStereoscopicEye.Right)
|
||||||
|
SetResetAfterRenderFlag(__instance, !TransformHiderManager.s_UseCloneToCullUi); // dont undo if ui culling
|
||||||
|
|
||||||
|
return;
|
||||||
|
void SetResetAfterRenderFlag(MonoBehaviour headHiderManager, bool flag)
|
||||||
|
{
|
||||||
|
if (headHiderManager is LocalCloneManager localCloneManager)
|
||||||
|
localCloneManager._resetAfterThisRender = flag;
|
||||||
|
else if (headHiderManager is TransformHiderManager transformHiderManager)
|
||||||
|
transformHiderManager._resetAfterThisRender = flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
LegacyContentMitigation/Properties/AssemblyInfo.cs
Normal file
32
LegacyContentMitigation/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using NAK.LegacyContentMitigation.Properties;
|
||||||
|
using MelonLoader;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||||
|
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||||
|
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||||
|
[assembly: AssemblyTitle(nameof(NAK.LegacyContentMitigation))]
|
||||||
|
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||||
|
[assembly: AssemblyProduct(nameof(NAK.LegacyContentMitigation))]
|
||||||
|
|
||||||
|
[assembly: MelonInfo(
|
||||||
|
typeof(NAK.LegacyContentMitigation.LegacyContentMitigationMod),
|
||||||
|
nameof(NAK.LegacyContentMitigation),
|
||||||
|
AssemblyInfoParams.Version,
|
||||||
|
AssemblyInfoParams.Author,
|
||||||
|
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/LegacyContentMitigation"
|
||||||
|
)]
|
||||||
|
|
||||||
|
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||||
|
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||||
|
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||||
|
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
|
||||||
|
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
|
||||||
|
[assembly: HarmonyDontPatchAll]
|
||||||
|
|
||||||
|
namespace NAK.LegacyContentMitigation.Properties;
|
||||||
|
internal static class AssemblyInfoParams
|
||||||
|
{
|
||||||
|
public const string Version = "1.0.0";
|
||||||
|
public const string Author = "Exterrata & NotAKidoS";
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue