dwelling act 4 (live motion cap w/ kinect azure)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
20 KiB

1 year ago
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace FullscreenEditor {
/// <summary>Clone of the internal UnityEditor.ShowMode.</summary>
public enum ShowMode {
/// <summary>Show as a normal window with max, min & close buttons.</summary>
NormalWindow = 0,
/// <summary>Used for a popup menu. On mac this means light shadow and no titlebar.</summary>
PopupMenu = 1,
/// <summary>Utility window - floats above the app. Disappears when app loses focus.</summary>
Utility = 2,
/// <summary>Window has no shadow or decorations. Used internally for dragging stuff around.</summary>
NoShadow = 3,
/// <summary>The Unity main window. On mac, this is the same as NormalWindow, except window doesn't have a close button.</summary>
MainWindow = 4,
/// <summary>Aux windows. The ones that close the moment you move the mouse out of them.</summary>
AuxWindow = 5,
/// <summary>Like PopupMenu, but without keyboard focus.</summary>
Tooltip = 6,
// Show as fullscreen window
Fullscreen = 8
}
/// <summary>Helper class for suppressing unity logs when calling a method that may show unwanted logs.</summary>
internal class SuppressLog : IDisposable {
private readonly bool lastState;
internal static ILogger Logger {
get {
#if UNITY_2017_1_OR_NEWER
return Debug.unityLogger;
#else
return Debug.logger;
#endif
}
}
public SuppressLog() {
lastState = Logger.logEnabled;
Logger.logEnabled = false;
}
public void Dispose() {
Logger.logEnabled = lastState;
}
}
/// <summary>Miscellaneous utilities for Fullscreen Editor.</summary>
[InitializeOnLoad]
public static class FullscreenUtility {
/// <summary>Contains a Texture2D icon loaded from a base64.</summary>
public class Icon {
private string m_base64;
private Texture2D m_texture;
public Texture2D Texture {
get { return m_texture ? m_texture : (m_texture = FindOrLoadTexture(m_base64)); }
}
public Icon(string base64) {
m_base64 = base64;
}
public Icon(string base64, string proVariantBase64) {
m_base64 = EditorGUIUtility.isProSkin ? proVariantBase64 : base64;
}
public static implicit operator Texture2D(Icon icon) {
return icon.Texture;
}
}
private static Vector2 mousePosition;
public static bool IsWindows { get { return Application.platform == RuntimePlatform.WindowsEditor; } }
public static bool IsMacOS { get { return Application.platform == RuntimePlatform.OSXEditor; } }
public static bool IsLinux { get { return Application.platform == RuntimePlatform.LinuxEditor; } }
static FullscreenUtility() {
var lastUpdate = EditorApplication.timeSinceStartup;
EditorApplication.update += () => {
if(EditorApplication.timeSinceStartup - lastUpdate > 0.5f && FullscreenPreferences.RectSource.Value == RectSourceMode.AtMousePosition) {
EditorApplication.RepaintHierarchyWindow();
EditorApplication.RepaintProjectWindow();
lastUpdate = EditorApplication.timeSinceStartup;
}
};
EditorApplication.hierarchyWindowItemOnGUI += (rect, id) => RecalculateMousePosition();
EditorApplication.projectWindowItemOnGUI += (rect, id) => RecalculateMousePosition();
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui += sceneView => RecalculateMousePosition();
#else
SceneView.onSceneGUIDelegate += sceneView => RecalculateMousePosition();
#endif
}
private static void RecalculateMousePosition() {
mousePosition = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
}
/// <summary>Returns wheter the extension is running with debugging enabled.</summary>
public static bool DebugModeEnabled {
get {
#if FULLSCREEN_DEBUG
return true;
#else
return false;
#endif
}
}
/// <summary>The mouse position, can be called outside an OnGUI method.</summary>
public static Vector2 MousePosition { get { return mousePosition; } }
/// <summary>The icon of this plugin.</summary>
public static readonly Icon FullscreenIcon = new Icon(
"iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAMAAABgZ9sFAAAApVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+4/eNVAAAANnRSTlMA/AfHA2sLtBTy8MxYO/nk9u3Qj06wNCceGeCppF9LPhDot5iBewHYn1JDQS8sIyCJdkdluZF8FV+iAAAB6klEQVRIx+2SWbKbMBBFJcAYC8xswAMznme/pPe/tNASLgImVH5TeefLvhxaouuS/xTpHrwi0nJ/BZE04O2IeomrcqHnTL+RN4HLLH16qOJr/53YYiYI9pcmS/MmMdk+6OoTaLHOPErsNpqlPZ2KnIJpMzerk0x3bAq00efDujw5BuFNws+OwsCY7Md169SJfTaug/nVhsoWYFRHtkqTqZ4I6LA+e1j8qScJe8NHuB4M6+brtgJkreJmZPHty/hjkWLJpk9CmU/UJaLxk8y1RgwAcELSxbfA9gnR+A3WClGnAMAqlaC+OpE+FxcSvPGWwkbBHwXkxx1BXQ7JJ8upz1s5qS+ARAdRBvw/iDSYquSfRflDbCz4k6wyhGEUGl+Y7g9t5QlT1O4LyHlBjgAF7ltzaSx9LH1DAaenOpY+qGfPsF5pretgP3rL3G0AUDcsQOrjRdVZjDpA2bFFQ35ITxMdmmD0Jbo+yVDXezdHXcZmt31KHEA8Fx996G/283c4F+2nY7q8bNOry6MRfd3ZmVaM67OHMQ+jDHe1vF1T4ycd1SlQ07F8jLycmZQOTsdFthw0jKIFIMPTD8yGhlJpRpTvxHaKfsHux225kFcOq9rMc5yVvPGeSUR2Q6VcXk7z3888n68a+eav+AWMDWJNSUXp9QAAAABJRU5ErkJggg=="
);
/// <summary>A smaller icon of this plugin.</summary>
public static readonly Icon FullscreenIconSmall = new Icon(
"iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAAclBMVEUAAABmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmbn+Ue6AAAAJXRSTlMABfscDPdX3LmNgF9IKxPtoXdvQxiumWlTJPLjkYcx6M3KxrM+6Owz4AAAAKJJREFUCNdlzlkOwyAMBNDBkECALDRkb9LV979infazliz5eX4GS/Cd6wFE1/mQcWHm4qmhH4Vc5WmaGwXVBPq5pTYCmFdqxRNfPFOJwJx2sX55pK3CaCdUQwZMhtJKFtAG/5PVNzeS1wo47rWqbG+cLUHJoOLD8SY/pnijN67csl0A5bkoqBfzuki/xnTM4vHs3xlEe/aPqKd9GGoAwY1pbj58lQvfBIytyAAAAABJRU5ErkJggg==",
"iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAAclBMVEUAAAC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSyGfY5AAAAJXRSTlMABfsM91ccGNy5jYBfSCsT7aF3b0OumWlTJPLjkYcx6M3KxrM+ZTSY8wAAAKJJREFUCNdljtsSgyAMRJegAgIqeNdqr/n/X2ycPjYzO5Oz+3KwBp/cACC65INBz8zFy6B6FvKVF9PSKqg20I876iKAZaNOeObeM5UIzPkQrt4eea8x2Rn1aABtoColASqN/zOySbTsjQLOR6NqO2hnS1DWqPl0vEvHFO/0wY07tiugPBcFDcK8reLX6sQsPF3+SSPayz+imY9xbAAEN+Wl/QKAZAvuNVnEigAAAABJRU5ErkJggg=="
);
/// <summary>The icon to show on the game view toolbar when fullscreen on play is enabled.</summary>
public static readonly Icon FullscreenOnPlayIcon = new Icon(
"iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAZlBMVEUAAAAZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRnaqT0eAAAAIXRSTlMA9rSVTunBWTwgFNzOrvvYo4x1ZGA4MALTx6eae3dTCgkQ40nLAAAAcklEQVQY053OORaFIBBE0WpsBQSchz+qvf9NSmCHJlZ4g3cKj8ddbD3q6pWmSwqRnjH/SIxKQ1WN4yOkMpSuDKuhLX41ZOEt3IJxxe0mLA7Ww0LLb87NkGvDJYYk7fkPNYWK0H8G9yIqY2rzHx9ix3i6E/A3BM5M0CXHAAAAAElFTkSuQmCC",
"iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAY1BMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+aRQ2gAAAAIHRSTlMA9rSV6cFZPCAU3M6uT/vYo4x1ZGBMODAJ08enmnt3U4mFYqQAAABvSURBVBjTnc43AoAwDATBkwHbOJBz1P9fiQvUUqByitXh98XG1xZlsYbhlYy5HTGdxEqkoqLEsjOJdLnJHRTd/pCQhtUwM/qPZwNmA22hIeUtpqZLte4VRRyWtIeqTITpmjC2zCJ9qNMe63wT8fce2Z8EsL04YKYAAAAASUVORK5CYII="
);
/// <summary>Show the notification of a newly opened <see cref="FullscreenView"/> about using the shortcut to close.</summary>
/// <param name="window">The fullscreen window.</param>
/// <param name="menuItemPath">The <see cref="MenuItem"/> path containing a shortcut.</param>
internal static void ShowFullscreenExitNotification(EditorWindow window, string menuItemPath) {
if(FullscreenPreferences.DisableNotifications)
return;
var notification = string.Format("Press {0} to exit fullscreen", TextifyMenuItemShortcut(menuItemPath));
ShowFullscreenNotification(window, notification);
}
/// <summary>Show a fullscreen notification in an <see cref="EditorWindow"/>.</summary>
/// <param name="window">The host of the notification.</param>
/// <param name="message">The message to show.</param>
public static void ShowFullscreenNotification(EditorWindow window, string message) {
if(!window)
return;
window.ShowNotification(new GUIContent(message, FullscreenIcon));
window.Repaint();
if(EditorWindow.mouseOverWindow) // This definitely made sense when I made it, so I won't remove
EditorWindow.mouseOverWindow.Repaint();
}
/// <summary>Does the given <see cref="MenuItem"/> path contains a key binding?</summary>
public static bool MenuItemHasShortcut(string menuItemPath) {
var index = menuItemPath.LastIndexOf(" ");
if(index++ == -1)
return false;
var shortcut = menuItemPath.Substring(index).Replace("_", "");
var evt = Event.KeyboardEvent(shortcut);
shortcut = InternalEditorUtility.TextifyEvent(evt);
return !shortcut.Equals("None", StringComparison.InvariantCultureIgnoreCase);
}
/// <summary>Gets a human-readable shortcut.</summary>
/// <param name="menuItemPath">The <see cref="MenuItem"/> path containing a shortcut.</param>
/// <returns></returns>
public static string TextifyMenuItemShortcut(string menuItemPath) {
var index = menuItemPath.LastIndexOf(" ");
if(index++ == -1)
return "None";
var shortcut = menuItemPath.Substring(index).Replace("_", "");
var evt = Event.KeyboardEvent(shortcut);
shortcut = InternalEditorUtility.TextifyEvent(evt);
return shortcut;
}
private static Texture2D FindOrLoadTexture(string base64) {
var found = GetRef<Texture2D>(base64);
return found ?
found :
LoadTexture(base64);
}
private static Texture2D LoadTexture(string base64) {
try {
var texture = new Texture2D(1, 1, TextureFormat.ARGB32, false, true);
var bytes = Convert.FromBase64String(base64);
texture.name = base64;
texture.hideFlags = HideFlags.HideAndDontSave;
texture.LoadImage(bytes);
return texture;
} catch(Exception e) {
Logger.Error("Failed to load texture: {0}", e);
return null;
}
}
/// <summary>Find an object by it's name and type.</summary>
/// <param name="name">The name of the object to search for.</param>
/// <typeparam name="T">The type of the object to search for.</typeparam>
public static T GetRef<T>(string name) where T : UnityObject {
return Resources.FindObjectsOfTypeAll<T>().FirstOrDefault(obj => obj.name == name);
}
/// <summary>Find an object by it's name and type.</summary>
/// <param name="name">The name of the object to search for.</param>
/// <param name="type">The type of the object to search for.</param>
public static UnityObject GetRef(Type type, string name) {
return Resources.FindObjectsOfTypeAll(type).FirstOrDefault(obj => obj.name == name);
}
/// <summary>Get the main view.</summary>
public static ScriptableObject GetMainView() {
var containers = Resources.FindObjectsOfTypeAll(Types.MainView);
if(containers.Length > 0)
return containers[0] as ScriptableObject;
throw new Exception("Couldn't find main view");
}
/// <summary>Get the main game view.</summary>
public static EditorWindow GetMainGameView() {
if(Types.GameView.HasMethod("GetMainGameView")) { // Removed in 2019.3 alpha
return Types.GameView.InvokeMethod<EditorWindow>("GetMainGameView");
} else if(Types.PreviewEditorWindow.HasMethod("GetMainPreviewWindow")) { // Removed in 2019.3 beta
return Types.PreviewEditorWindow.InvokeMethod<EditorWindow>("GetMainPreviewWindow");
} else { // if (Types.PlayModeView.HasMethod("GetMainPlayModeView"))
return Types.PlayModeView.InvokeMethod<EditorWindow>("GetMainPlayModeView");
}
}
/// <summary>Get all the game views. This returns even the docked game views which are not visible.</summary>
public static EditorWindow[] GetGameViews() {
return Resources
.FindObjectsOfTypeAll(Types.GameView)
.Cast<EditorWindow>()
.ToArray();
}
/// <summary>Returns the focused view if it is a dock area with more than one tab, otherwise, returns the focused window.</summary>
public static ScriptableObject GetFocusedViewOrWindow() {
var mostSpecificView = Types.GUIView.GetPropertyValue<ScriptableObject>("focusedView");
if(!mostSpecificView)
return null;
// The most specific obj is a window, open it instead of the view
if(mostSpecificView.IsOfType(Types.HostView, false))
return EditorWindow.focusedWindow;
var viewHierarchy = GetViewHierarchy(mostSpecificView);
var leastSpecificView = viewHierarchy.LastOrDefault();
// The view hierarchy has the same length of all the views in this ContainerWindow
// This means there are no cousins views handled by a split group on the surroundings of this one
// So, we're alone on the container
var alone = leastSpecificView.GetPropertyValue<Array>("allChildren").Length == viewHierarchy.Length;
if(alone && EditorWindow.focusedWindow.InvokeMethod<int>("GetNumTabs") > 1)
alone = false; // But, we may not be the only tab on the host view
// If the focused view is in the main view, or we are the only child on this view, then we open the window
return alone || leastSpecificView.IsOfType(Types.MainView) ?
EditorWindow.focusedWindow :
leastSpecificView;
}
/// <summary>Returns all the parents of a given view.</summary>
public static ScriptableObject[] GetViewHierarchy(ScriptableObject view) {
if(!view)
return new ScriptableObject[0];
view.EnsureOfType(Types.View);
var list = new List<ScriptableObject>() { view };
var parent = view.GetPropertyValue<ScriptableObject>("parent");
while(parent) { // Get the least specific view
view = parent;
list.Add(view);
parent = view.GetPropertyValue<ScriptableObject>("parent");
}
return list.ToArray();
}
/// <summary>Get all the children view of a given view.</summary>
public static ScriptableObject[] GetAllViewChildren(ScriptableObject view) {
if(!view)
return new ScriptableObject[0];
view.EnsureOfType(Types.View);
return view.GetPropertyValue<Array>("allChildren")
.Cast<ScriptableObject>()
.ToArray();
}
/// <summary>Returns wheter a given view is focused or not.</summary>
public static bool IsViewFocused(ScriptableObject view) {
if(!view)
return false;
view.EnsureOfType(Types.View);
var focused = Types.GUIView.GetPropertyValue<ScriptableObject>("focusedView");
var children = GetAllViewChildren(view);
return children.Contains(focused);
}
/// <summary>Focus a view.</summary>
public static void FocusView(ScriptableObject guiView) {
if(!guiView)
return;
// guiView.EnsureOfType(Types.GUIView);
if(guiView.IsOfType(Types.GUIView))
guiView.InvokeMethod("Focus");
else {
var vp = new ViewPyramid(guiView);
var vc = vp.Container;
var methodName = "Internal_BringLiveAfterCreation";
if(vc) {
if(vc.HasMethod(methodName, new Type[] { typeof(bool), typeof(bool), typeof(bool) }))
// displayImmediately, setFocus, showMaximized
vc.InvokeMethod(methodName, false, true, false);
else
// displayImmediately, setFocus
vc.InvokeMethod(methodName, false, true);
}
}
}
/// <summary> Returns the display scaling of the editor, e.g. 1.5 if 125%</summary>
public static float GetDisplayScaling() {
return EditorGUIUtility.pixelsPerPoint;
}
/// <summary> Returns a screen rect corrected to fit the editor scaling</summary>
public static Rect DpiCorrectedArea(Rect area) {
var scaling = GetDisplayScaling();
area.width /= scaling;
area.height /= scaling;
return area;
}
/// <summary>Get the default height of the editor toolbars.</summary>
public static float GetToolbarHeight() {
try {
if(typeof(EditorGUI).HasField("kWindowToolbarHeight")) {
var result = typeof(EditorGUI).GetFieldValue<object>("kWindowToolbarHeight");
if(result is int)
return (int)result;
else
return result.GetPropertyValue<float>("value");
} else
return 17f; // Default on < 2019.3 versions
} catch(Exception e) {
if(FullscreenUtility.DebugModeEnabled)
Debug.LogException(e);
return 17f;
}
}
/// <summary>Set the default height of the editor toolbars.</summary>
public static bool SetToolbarHeight(float value) {
try {
// On Unity blow 2019.3 this is a const field and cannot be changed
if(typeof(EditorGUI).HasField("kWindowToolbarHeight")) {
var result = typeof(EditorGUI).GetFieldValue<object>("kWindowToolbarHeight");
result.SetFieldValue("m_Value", value);
return true;
} else {
return false;
}
} catch(Exception e) {
if(FullscreenUtility.DebugModeEnabled)
Debug.LogException(e);
return false;
}
}
/// <summary>Set Game View target display.</summary>
public static void SetGameViewDisplayTarget(EditorWindow gameView, int display) {
gameView.EnsureOfType(Types.GameView);
if(gameView.HasProperty("targetDisplay")) {
gameView.SetPropertyValue("targetDisplay", display);
} else if(gameView.HasField("m_TargetDisplay")) {
gameView.SetFieldValue("m_TargetDisplay", display);
} else {
Logger.Error("Could not set Game View target display");
}
}
}
}