From 252521061bf68172a1bdd6df158205b9b869e75b Mon Sep 17 00:00:00 2001 From: originalnicodr Date: Sat, 27 Jan 2024 17:10:15 -0300 Subject: [PATCH] Added misc panel with HUD toggle and screenshot support. Added more hotkeys and changed the order of panels in the navbar. --- src/ExplorerBehaviour.cs | 21 ++++ src/UI/Panels/FreeCamPanel.cs | 4 +- src/UI/Panels/MiscPanel.cs | 223 ++++++++++++++++++++++++++++++++++ src/UI/UIManager.cs | 10 +- src/UI/Widgets/CatmullRom.cs | 9 +- 5 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 src/UI/Panels/MiscPanel.cs diff --git a/src/ExplorerBehaviour.cs b/src/ExplorerBehaviour.cs index f508fc2..fcffac6 100644 --- a/src/ExplorerBehaviour.cs +++ b/src/ExplorerBehaviour.cs @@ -102,6 +102,7 @@ namespace UnityExplorer // Continous checks and actions stopFrameSkip(); maybeForcePause(); + UIManager.GetPanel(UIManager.Panels.Misc).MaybeTakeScreenshot(); if (InputManager.GetKeyDown(KeyCode.Pause)) { @@ -116,6 +117,26 @@ namespace UnityExplorer frameSkip = true; } } + + if (InputManager.GetKeyDown(KeyCode.F12)) + { + UIManager.GetPanel(UIManager.Panels.Misc).screenshotStatus = UnityExplorer.UI.Panels.Misc.ScreenshotState.TurnOffUI; + } + + if (InputManager.GetKeyDown(KeyCode.Delete)) + { + UIManager.GetPanel(UIManager.Panels.Misc).ToggleHUDElements(); + } + + if (InputManager.GetKeyDown(KeyCode.Insert)) + { + FreeCamPanel.StartStopButton_OnClick(); + } + + if (InputManager.GetKeyDown(KeyCode.Home)) + { + FreeCamPanel.blockFreecamMovementToggle.isOn = !FreeCamPanel.blockFreecamMovementToggle.isOn; + } } void stopFrameSkip(){ diff --git a/src/UI/Panels/FreeCamPanel.cs b/src/UI/Panels/FreeCamPanel.cs index ec5792a..ac0f211 100644 --- a/src/UI/Panels/FreeCamPanel.cs +++ b/src/UI/Panels/FreeCamPanel.cs @@ -311,7 +311,7 @@ namespace UnityExplorer.UI.Panels return row; } - void StartStopButton_OnClick() + public static void StartStopButton_OnClick() { EventSystemHelper.SetSelectedGameObject(null); @@ -342,7 +342,7 @@ namespace UnityExplorer.UI.Panels } } - void SetToggleButtonState() + static void SetToggleButtonState() { if (inFreeCamMode) { diff --git a/src/UI/Panels/MiscPanel.cs b/src/UI/Panels/MiscPanel.cs new file mode 100644 index 0000000..38692da --- /dev/null +++ b/src/UI/Panels/MiscPanel.cs @@ -0,0 +1,223 @@ +using UnityExplorer.CacheObject; +using UnityExplorer.Inspectors; +using UnityExplorer.ObjectExplorer; +using UniverseLib.UI; +using UniverseLib.UI.Models; +using UniverseLib.UI.ObjectPool; +#if UNHOLLOWER +using UnhollowerRuntimeLib; +#endif +#if INTEROP +using Il2CppInterop.Runtime.Injection; +#endif + +namespace UnityExplorer.UI.Panels +{ + public class Misc : UEPanel + { + public enum ScreenshotState + { + DoNothing, + TurnOffUI, + TakeScreenshot, + TurnOnUI, + } + + public class UIElement + { + public UIElement(object obj) + { + Object = obj; + + ReflectionInspector inspector = Pool.Borrow(); + inspector.Target = obj.TryCast(); + Type objType = obj is Type type ? type : obj.GetActualType(); + List members = CacheMemberFactory.GetCacheMembers(objType, inspector); + + foreach (CacheMember member in members){ + if (member.NameForFiltering.EndsWith(".enabled")){ + member.Evaluate(); + Enabled = member; + } + + if (member.NameForFiltering.EndsWith(".name")){ + member.Evaluate(); + Name = member; + } + + if (Enabled != null && Name != null) break; + } + } + + public object Object { get; } + public CacheMember Enabled { get; } + public CacheMember Name { get; } + + public override string ToString() => $"{Object}"; + } + + public Misc(UIBase owner) : base(owner) + { + captureScreenshotFunction = null; + FindCaptureScreenshotFunction(); + superSizeValue = 2; + hud = new List(); + + screenshotStatus = ScreenshotState.DoNothing; + } + + public override string Name => "Misc"; + public override UIManager.Panels PanelType => UIManager.Panels.Misc; + public override int MinWidth => 275; + public override int MinHeight => 100; + public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f); + public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f); + public override bool NavButtonWanted => true; + public override bool ShouldSaveActiveState => true; + + List hud; + Toggle HUDToggle; + + CacheMethod captureScreenshotFunction; + int superSizeValue; + public ScreenshotState screenshotStatus; + + private void LoadHUDElements(){ + List currentResults = SearchProvider.UnityObjectSearch("", "UnityEngine.Canvas", ChildFilter.Any, SceneFilter.Any); + if (hud.Count > 0){ + SetValueHUDElements(true); + HUDToggle.isOn = true; + hud.Clear(); + } + + foreach (object obj in currentResults){ + UIElement entry = new UIElement(obj); + // Ignore non-enabled objects and objects without a name, since those tend to be irrelevant + if ((bool) entry.Enabled.Value && entry.Name.Value.ToString() != ""){ + hud.Add(entry); + } + } + } + + private void SetValueHUDElements(bool value){ + // If its the first time changing the HUD, we look up for UI elements. + if (hud.Count == 0){ + LoadHUDElements(); + } + + foreach (UIElement elem in hud){ + elem.Enabled.TrySetUserValue(value); + } + } + + public void ToggleHUDElements(){ + HUDToggle.isOn = !HUDToggle.isOn; + SetValueHUDElements(HUDToggle.isOn); + } + + private void TakeScreenshot(){ + MethodInfo methodInfo = captureScreenshotFunction.MethodInfo; + string filename = DateTime.Now.ToString("yyyy-M-d HH-mm-ss"); + + ExplorerCore.LogWarning(filename); + + string screenshotsPath = Path.Combine(ExplorerCore.ExplorerFolder, "Screenshots"); + System.IO.Directory.CreateDirectory(screenshotsPath); + + object[] args = {$"{screenshotsPath}\\{filename}.png", superSizeValue}; + + methodInfo.Invoke(captureScreenshotFunction.DeclaringInstance, args); + } + + private void FindCaptureScreenshotFunction(){ + List results = SearchProvider.ClassSearch("UnityEngine.ScreenCapture"); + // We assume it's the first for now. Come back if we need to do something else to get it. + object obj = results[0]; + Type objType = obj is Type type ? type : obj.GetActualType(); + ReflectionInspector inspector = Pool.Borrow(); + List members = CacheMemberFactory.GetCacheMembers(objType, inspector); + foreach (CacheMember member in members){ + if (member is CacheMethod methodMember && methodMember.NameForFiltering == "ScreenCapture.CaptureScreenshot(string, int)"){ + captureScreenshotFunction = methodMember; + break; + } + } + } + + // We use an enum to walk a series of steps in each frame, so we can take the screenshot without UnityExplorer UI. + public void MaybeTakeScreenshot(){ + switch (screenshotStatus){ + case ScreenshotState.TurnOffUI: + screenshotStatus = ScreenshotState.TakeScreenshot; + UIManager.ShowMenu = false; + break; + case ScreenshotState.TakeScreenshot: + TakeScreenshot(); + screenshotStatus = ScreenshotState.TurnOnUI; + break; + case ScreenshotState.TurnOnUI: + screenshotStatus = ScreenshotState.DoNothing; + UIManager.ShowMenu = true; + break; + case ScreenshotState.DoNothing: + default: + break; + } + } + + // ~~~~~~~~ UI construction / callbacks ~~~~~~~~ + + protected override void ConstructPanelContent() + { + // HUD toggle + GameObject HUDhoriGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "HUDhoriGroup", false, false, true, true, 3, + default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft); + UIFactory.SetLayoutElement(HUDhoriGroup, minHeight: 25, flexibleWidth: 9999); + + ButtonRef loadHUDElements = UIFactory.CreateButton(HUDhoriGroup, "HideUI", "Load HUD elements"); + UIFactory.SetLayoutElement(loadHUDElements.GameObject, minWidth: 150, minHeight: 25); + loadHUDElements.OnClick += LoadHUDElements; + + HUDToggle = new Toggle(); + GameObject HUDToggleObj = UIFactory.CreateToggle(HUDhoriGroup, "Toggle HUD", out HUDToggle, out Text toggleText); + UIFactory.SetLayoutElement(HUDToggleObj, minHeight: 25); + HUDToggle.onValueChanged.AddListener(SetValueHUDElements); + HUDToggle.isOn = true; // we picked up only the active effects + toggleText.text = "Toggle HUD"; + + // Screenshot function + GameObject TakeScreenshotHoriGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "Take screenshot", false, false, true, true, 3, + default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft); + UIFactory.SetLayoutElement(TakeScreenshotHoriGroup, minHeight: 25, flexibleWidth: 9999); + + ButtonRef takeScreenshot = UIFactory.CreateButton(TakeScreenshotHoriGroup, "TakeScreenshot", "Take screenshot"); + UIFactory.SetLayoutElement(takeScreenshot.GameObject, minWidth: 150, minHeight: 25); + takeScreenshot.OnClick += () => screenshotStatus = ScreenshotState.TurnOffUI; + + AddInputField(TakeScreenshotHoriGroup, "Supersize", "Supersize:", $"{2}", SuperSize_OnEndEdit); + } + + GameObject AddInputField(GameObject parent, string name, string labelText, string placeHolder, Action onInputEndEdit) + { + Text posLabel = UIFactory.CreateLabel(parent, $"{name}_Label", labelText); + UIFactory.SetLayoutElement(posLabel.gameObject, minWidth: 75, minHeight: 25); + + InputFieldRef inputField = UIFactory.CreateInputField(parent, $"{name}_Input", placeHolder); + UIFactory.SetLayoutElement(inputField.GameObject, minWidth: 25, minHeight: 25); + inputField.Component.GetOnEndEdit().AddListener(onInputEndEdit); + + return parent; + } + + void SuperSize_OnEndEdit(string input) + { + if (!ParseUtility.TryParse(input, out int parsed, out Exception parseEx)) + { + ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}"); + return; + } + + superSizeValue = parsed; + } + } +} diff --git a/src/UI/UIManager.cs b/src/UI/UIManager.cs index 290a964..c4ca394 100644 --- a/src/UI/UIManager.cs +++ b/src/UI/UIManager.cs @@ -25,7 +25,8 @@ namespace UnityExplorer.UI Freecam, LightsManager, CamPaths, - PostProcessingPanel + PostProcessingPanel, + Misc, } public enum VerticalAnchor @@ -47,7 +48,7 @@ namespace UnityExplorer.UI public static RectTransform NavBarRect; public static GameObject NavbarTabButtonHolder; - private static readonly Vector2 NAVBAR_DIMENSIONS = new(1400f, 35f); + private static readonly Vector2 NAVBAR_DIMENSIONS = new(1500f, 35f); private static ButtonRef closeBtn; private static TimeScaleWidget timeScaleWidget; @@ -92,12 +93,13 @@ namespace UnityExplorer.UI UIPanels.Add(Panels.Inspector, new InspectorPanel(UiBase)); UIPanels.Add(Panels.CSConsole, new CSConsolePanel(UiBase)); UIPanels.Add(Panels.HookManager, new HookManagerPanel(UiBase)); + UIPanels.Add(Panels.Clipboard, new ClipboardPanel(UiBase)); + UIPanels.Add(Panels.ConsoleLog, new LogPanel(UiBase)); UIPanels.Add(Panels.Freecam, new FreeCamPanel(UiBase)); UIPanels.Add(Panels.LightsManager, new LightsManager(UiBase)); UIPanels.Add(Panels.CamPaths, new CamPaths(UiBase)); UIPanels.Add(Panels.PostProcessingPanel, new PostProcessingPanel(UiBase)); - UIPanels.Add(Panels.Clipboard, new ClipboardPanel(UiBase)); - UIPanels.Add(Panels.ConsoleLog, new LogPanel(UiBase)); + UIPanels.Add(Panels.Misc, new UnityExplorer.UI.Panels.Misc(UiBase)); UIPanels.Add(Panels.Options, new OptionsPanel(UiBase)); UIPanels.Add(Panels.UIInspectorResults, new MouseInspectorResultsPanel(UiBase)); diff --git a/src/UI/Widgets/CatmullRom.cs b/src/UI/Widgets/CatmullRom.cs index 3659651..bf1d402 100644 --- a/src/UI/Widgets/CatmullRom.cs +++ b/src/UI/Widgets/CatmullRom.cs @@ -228,12 +228,13 @@ namespace UnityExplorer.CatmullRom bool closedLoopFinalPoint = (closedLoop && currentPoint == splinePoints.Length - 1); - //Had tu add "splinePoints.Length" because C# mod doesnt work well with negative values + // Had tu add "splinePoints.Length" because C# mod doesnt work well with negative values int previousPoint = closedLoop ? (currentPoint + splinePoints.Length - 1)%(splinePoints.Length) : System.Math.Max(currentPoint - 1, 0); int endSegmentPoint = closedLoop ? (currentPoint + 1)%(splinePoints.Length) : System.Math.Min(currentPoint + 1, splinePoints.Length - 1); int nextPoint = closedLoop ? (currentPoint + 2)%(splinePoints.Length) : System.Math.Min(currentPoint + 2, splinePoints.Length - 1); - if (d >= 1) return closedLoop ? splinePoints[0] : splinePoints[splinePoints.Length - 1]; //Ideally we should really loop over tho, maybe d % 1. + // Ideally we should really loop over tho. + if (d >= 1) return closedLoop ? splinePoints[0] : splinePoints[splinePoints.Length - 1]; p0 = splinePoints[previousPoint].position; r0 = CatmullRomPoint.QuaternionToVector4(splinePoints[previousPoint].rotation); @@ -247,7 +248,7 @@ namespace UnityExplorer.CatmullRom p3 = splinePoints[nextPoint].position; r3 = CatmullRomPoint.QuaternionToVector4(splinePoints[nextPoint].rotation); - //Check if we are using the shortest path on the rotation. If not, change each rotation to represent that shortest path. + // Check if we are using the shortest path on the rotation. If not, change each rotation to represent that shortest path. if (Vector4.Dot(r0, r1) < 0) r1 = - r1; @@ -277,7 +278,7 @@ namespace UnityExplorer.CatmullRom return new CatmullRomPoint(position, rotation, fov); } - //Implementation from: https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html + // Implementation from: https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html private Vector3 CatmullRomInterpolation(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t){ float tension = 0; float alpha = 0.5f;