Add ultimate xr

This commit is contained in:
2024-08-06 21:58:35 +02:00
parent 864033bf10
commit 7165bacd9d
3952 changed files with 2162037 additions and 35 deletions

View File

@@ -0,0 +1,33 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ComponentProcessorWindow.LogOptions.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
namespace UltimateXR.Editor.Utilities
{
public abstract partial class ComponentProcessorWindow<T>
{
#region Public Types & Data
/// <summary>
/// Enumerates the different log option flags.
/// </summary>
[Flags]
public enum LogOptions
{
/// <summary>
/// Output information of processed components.
/// </summary>
Processed = 1 << 0,
/// <summary>
/// Output information of components that were not processed (ignored).
/// </summary>
Ignored = 1 << 1
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f37fd639290e419f8576383bcc1a17a0
timeCreated: 1687933537

View File

@@ -0,0 +1,40 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ComponentProcessorWindow.TargetObjects.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace UltimateXR.Editor.Utilities
{
public abstract partial class ComponentProcessorWindow<T>
{
#region Private Types & Data
/// <summary>
/// Enumerates the different potential target object(s) for the component processor.
/// </summary>
private enum TargetObjects
{
/// <summary>
/// Processes a single component.
/// </summary>
SingleComponent,
/// <summary>
/// Processes the current selection, and optionally the hierarchy below or inside the prefab.
/// </summary>
CurrentSelection,
/// <summary>
/// Processes the current scene.
/// </summary>
CurrentScene,
/// <summary>
/// Processes a whole folder and all sub-folders recursively.
/// </summary>
ProjectFolder,
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f2fe527f640e48848ff21be8b40af71a
timeCreated: 1675100896

View File

@@ -0,0 +1,428 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ComponentProcessorWindow.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using UltimateXR.Core;
using UltimateXR.Extensions.Unity;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace UltimateXR.Editor.Utilities
{
/// <summary>
/// Base editor window to create tools that process a type of component on a selection or even the whole project.
/// </summary>
public abstract partial class ComponentProcessorWindow<T> : EditorWindow where T : Component
{
#region Inspector Properties/Serialized Fields
[SerializeField] private TargetObjects _targetObjects = TargetObjects.ProjectFolder;
[SerializeField] private T _targetSingleObject = null;
[SerializeField] private string _startPath = "";
[SerializeField] private bool _ignoreUxrAssets = true;
[SerializeField] private LogOptions _logOptions = LogOptions.Processed;
[SerializeField] private bool _onlyCheck = false;
[SerializeField] private bool _recurseIntoChildren = true;
[SerializeField] private bool _recurseIntoPrefabs = true;
#endregion
#region Unity
/// <summary>
/// Loads the editor prefs
/// </summary>
protected virtual void OnEnable()
{
// TODO: Load editor prefs using a key based on typeof(T).FullName
}
/// <summary>
/// Saves the editor prefs
/// </summary>
protected virtual void OnDisable()
{
// TODO: Save editor prefs
}
/// <summary>
/// Draws the inspector
/// </summary>
private void OnGUI()
{
if (!string.IsNullOrEmpty(HelpBoxMessage))
{
EditorGUILayout.HelpBox(HelpBoxMessage, HelpBoxMessageType);
}
bool buttonEnabled = false;
bool showIgnoreUxrOption = CanProcessUltimateXRAssets;
_targetObjects = (TargetObjects)EditorGUILayout.EnumPopup(ContentTargetObjects, _targetObjects);
if (_targetObjects == TargetObjects.SingleComponent)
{
_targetSingleObject = EditorGUILayout.ObjectField(ContentTargetSingleObject, _targetSingleObject, typeof(T), true) as T;
_recurseIntoChildren = EditorGUILayout.Toggle(ContentRecurseIntoChildren, _recurseIntoChildren);
_recurseIntoPrefabs = EditorGUILayout.Toggle(ContentRecurseIntoPrefabs, _recurseIntoPrefabs);
buttonEnabled = _targetSingleObject != null;
}
else if (_targetObjects == TargetObjects.CurrentSelection)
{
GUI.enabled = Selection.gameObjects.Length > 0;
_recurseIntoChildren = EditorGUILayout.Toggle(ContentRecurseIntoChildren, _recurseIntoChildren);
_recurseIntoPrefabs = EditorGUILayout.Toggle(ContentRecurseIntoPrefabs, _recurseIntoPrefabs);
GUI.enabled = true;
buttonEnabled = Selection.gameObjects.Length > 0;
}
else if (_targetObjects == TargetObjects.CurrentScene)
{
buttonEnabled = SceneManager.sceneCount > 0;
_recurseIntoChildren = EditorGUILayout.Toggle(ContentRecurseIntoChildren, _recurseIntoChildren);
_recurseIntoPrefabs = EditorGUILayout.Toggle(ContentRecurseIntoPrefabs, _recurseIntoPrefabs);
}
else if (_targetObjects == TargetObjects.ProjectFolder)
{
EditorGUILayout.BeginHorizontal();
_startPath = EditorGUILayout.TextField(ContentPathStart, _startPath);
if (GUILayout.Button(ContentChooseFolder, GUILayout.ExpandWidth(false)) && UxrEditorUtils.OpenFolderPanel(out string path))
{
_startPath = path;
Repaint();
}
EditorGUILayout.EndHorizontal();
showIgnoreUxrOption = false;
buttonEnabled = true;
}
if (showIgnoreUxrOption)
{
EditorGUI.BeginChangeCheck();
_ignoreUxrAssets = EditorGUILayout.Toggle(ContentIgnoreUxrAssets, _ignoreUxrAssets);
if (EditorGUI.EndChangeCheck() && _ignoreUxrAssets == false && !string.IsNullOrEmpty(DontIgnoreUxrAssetsWarningMessage))
{
EditorUtility.DisplayDialog(UxrConstants.Editor.Warning, DontIgnoreUxrAssetsWarningMessage, UxrConstants.Editor.Ok);
}
}
_logOptions = (LogOptions)EditorGUILayout.EnumFlagsField(ContentLogOptions, _logOptions);
_onlyCheck = EditorGUILayout.Toggle(ContentOnlyCheck, _onlyCheck);
// Draw processor GUI if necessary
OnProcessorGUI();
// Bottom part
GUILayout.Space(30);
GUI.enabled = buttonEnabled && ProcessButtonEnabled;
if (UxrEditorUtils.CenteredButton(new GUIContent(_onlyCheck ? "Check" : ProcessButtonText)))
{
if (OnProcessStarting())
{
if (_targetObjects == TargetObjects.SingleComponent)
{
UxrEditorUtils.ModifyComponent<T>(_targetSingleObject,
CurrentComponentProcessingOptions,
ComponentProcessor,
progressInfo => EditorUtility.DisplayCancelableProgressBar(progressInfo.Title, progressInfo.Info, progressInfo.Progress),
out bool _,
_onlyCheck);
}
else if (_targetObjects == TargetObjects.CurrentSelection)
{
foreach (GameObject gameObject in Selection.gameObjects)
{
UxrEditorUtils.ModifyComponent<T>(gameObject,
CurrentComponentProcessingOptions,
ComponentProcessor,
progressInfo => EditorUtility.DisplayCancelableProgressBar(progressInfo.Title, progressInfo.Info, progressInfo.Progress),
out bool _,
_onlyCheck);
}
}
else if (_targetObjects == TargetObjects.CurrentScene)
{
for (int i = 0; i < SceneManager.sceneCount; ++i)
{
foreach (GameObject gameObject in SceneManager.GetSceneAt(i).GetRootGameObjects())
{
UxrEditorUtils.ModifyComponent<T>(gameObject,
CurrentComponentProcessingOptions,
ComponentProcessor,
progressInfo => EditorUtility.DisplayCancelableProgressBar(progressInfo.Title, progressInfo.Info, progressInfo.Progress),
out bool _,
_onlyCheck);
}
}
}
else if (_targetObjects == TargetObjects.ProjectFolder)
{
UxrEditorUtils.ProcessAllProjectComponents<T>(_startPath,
ComponentProcessor,
progressInfo => EditorUtility.DisplayCancelableProgressBar(progressInfo.Title, progressInfo.Info, progressInfo.Progress),
out bool _,
false,
_onlyCheck);
}
OnProcessEnded();
if (_targetObjects != TargetObjects.SingleComponent)
{
ShowResultsDialog(_prefabComponents.Sum(c => c.Value), _prefabComponents.Count, _sceneComponents.Sum(c => c.Value), _sceneComponents.Count);
}
}
}
GUI.enabled = true;
}
#endregion
#region Event Trigger Methods
/// <summary>
/// Overridable method called right before starting processing.
/// </summary>
/// <remarks>When overriden, the base needs to be called</remarks>
protected virtual bool OnProcessStarting()
{
_prefabComponents = new Dictionary<string, int>();
_sceneComponents = new Dictionary<string, int>();
return true;
}
/// <summary>
/// Overridable method called right after processing finished.
/// </summary>
/// <remarks>When overriden, the base needs to be called</remarks>
protected virtual void OnProcessEnded()
{
EditorUtility.ClearProgressBar();
AssetDatabase.SaveAssets();
}
/// <summary>
/// Draws the specific processor GUI.
/// </summary>
/// <remarks>When overriden, the base needs to be called</remarks>
protected virtual void OnProcessorGUI()
{
}
#endregion
#region Protected Methods
/// <summary>
/// Processes the component.
/// </summary>
/// <param name="info">Information of component to process</param>
/// <param name="onlyCheck">
/// Whether to only check if components should be processed, without making any changes. This
/// can be used to get how many elements would be changed without modifying any data
/// </param>
/// <param name="isChanged">Returns whether the component was changed</param>
/// <param name="forceNoLog">Returns whether to force the result not be logged</param>
protected abstract void ProcessComponent(UxrComponentInfo<T> info, bool onlyCheck, out bool isChanged, out bool forceNoLog);
/// <summary>
/// Overridable method that shows the results dialog.
/// </summary>
/// <param name="prefabComponentCount">Number of components in prefabs processed</param>
/// <param name="prefabCount">Number of prefabs processed</param>
/// <param name="sceneComponentCount">Number of components in scenes processed</param>
/// <param name="sceneCount">Scenes processed</param>
protected virtual void ShowResultsDialog(int prefabComponentCount, int prefabCount, int sceneComponentCount, int sceneCount)
{
string action = _onlyCheck ? "Found" : "Processed";
EditorUtility.DisplayDialog("Finished", $"{action} {prefabComponentCount} components in {prefabCount} prefabs and {sceneComponentCount} components in {sceneCount} scenes", UxrConstants.Editor.Ok);
}
#endregion
#region Private Methods
/// <summary>
/// Component processor.
/// </summary>
/// <param name="info">Contains the component to process</param>
/// <param name="onlyCheck">
/// Whether to only check if components should be processed, without making any changes. This
/// can be used to get how many elements would be changed without modifying any data
/// </param>
/// <returns>Whether the component required to be changed</returns>
private bool ComponentProcessor(UxrComponentInfo<T> info, bool onlyCheck)
{
ProcessComponent(info, onlyCheck, out bool isChanged, out bool ignoreLog);
if (!ignoreLog)
{
LogProcessing(info, isChanged);
}
return isChanged;
}
/// <summary>
/// Logs changes to the console.
/// </summary>
/// <param name="info">Change information</param>
/// <param name="isChanged">Whether the component was changed</param>
private void LogProcessing(UxrComponentInfo<T> info, bool isChanged)
{
string action = _onlyCheck ? "Found" : "Processed";
string processAction = isChanged ? action : _onlyCheck ? "Found to ignore" : "Ignored";
bool shouldLog = (isChanged && _logOptions.HasFlag(LogOptions.Processed)) || (!isChanged && _logOptions.HasFlag(LogOptions.Ignored));
if (info.TargetPrefab != null)
{
string path = AssetDatabase.GetAssetPath(info.TargetPrefab);
if (isChanged)
{
if (_prefabComponents.ContainsKey(path))
{
_prefabComponents[path]++;
}
else
{
_prefabComponents.Add(path, 1);
}
}
if (shouldLog)
{
string dataType = info.IsOriginalSource ? "original data source" : "not original data source";
Debug.Log($"{processAction} component {info.TargetComponent.GetPathUnderScene()} in prefab {info.TargetPrefab.name} ({dataType})");
}
}
else
{
string scenePath = info.TargetComponent.gameObject.scene.path;
if (isChanged)
{
if (_sceneComponents.ContainsKey(scenePath))
{
_sceneComponents[scenePath]++;
}
else
{
_sceneComponents.Add(scenePath, 1);
}
}
if (shouldLog)
{
string componentType = info.IsOriginalSource ? "original" : "instantiated";
Debug.Log($"{processAction} {componentType} component {info.TargetComponent.GetPathUnderScene()} in scene {scenePath}");
}
}
}
#endregion
#region Protected Types & Data
/// <summary>
/// Gets whether the component processor can process components from assets in UltimateXR folders.
/// </summary>
protected virtual bool CanProcessUltimateXRAssets => false;
/// <summary>
/// Gets the message to show in the help box. Null or empty for no message.
/// </summary>
protected virtual string HelpBoxMessage => string.Empty;
/// <summary>
/// Gets the type of message to show in the help box.
/// </summary>
protected virtual MessageType HelpBoxMessageType => MessageType.Info;
/// <summary>
/// Gets the message to show in the help box. Null or empty for no message.
/// </summary>
protected virtual string DontIgnoreUxrAssetsWarningMessage => "All assets in UltimateXR come with a predefined configuration. Changing it may have unwanted results";
/// <summary>
/// Gets the text to show on the process button.
/// </summary>
protected virtual string ProcessButtonText => "Process";
/// <summary>
/// Gets whether the process button is available.
/// </summary>
protected virtual bool ProcessButtonEnabled => true;
#endregion
#region Private Types & Data
private GUIContent ContentTargetObjects => new GUIContent("Target Objects", "The objects to change: objects in the current scene or prefabs in the whole project");
private GUIContent ContentTargetSingleObject => new GUIContent("Object To Process", "The object to process");
private GUIContent ContentPathStart => new GUIContent("Path Start", "If empty, it will process the whole /Assets folder. Use Assets/Application/Prefabs/ to start from this folder for example");
private GUIContent ContentChooseFolder => new GUIContent("...", "Selects the root folder to process");
private GUIContent ContentIgnoreUxrAssets => new GUIContent("Ignore UltimateXR assets", "Ignores processing assets in UltimateXR folders");
private GUIContent ContentLogOptions => new GUIContent("Log Options", "Whether to log components that were processed and components that were not processed (ignored)");
private GUIContent ContentOnlyCheck => new GUIContent("Only Log, Don't Modify", "Scared to proceed and make changes? This option will not make any modifications and instead will only log on the console which objects would be changed");
private GUIContent ContentRecurseIntoChildren => new GUIContent("Recurse Into Children", "Whether to process also child objects");
private GUIContent ContentRecurseIntoPrefabs => new GUIContent("Recurse Into Prefabs", "Whether to process also the same components in all parent prefabs if they exist");
/// <summary>
/// Gets whether to ignore components in assets in UltimateXR folders.
/// </summary>
private bool IgnoreUxrAssets => !CanProcessUltimateXRAssets || _ignoreUxrAssets;
/// <summary>
/// Gets the current component processing options flags.
/// </summary>
private UxrComponentProcessingOptions CurrentComponentProcessingOptions
{
get
{
UxrComponentProcessingOptions options = UxrComponentProcessingOptions.All;
if (IgnoreUxrAssets)
{
options &= ~UxrComponentProcessingOptions.ProcessUltimateXRAssetComponents;
}
if (_targetObjects == TargetObjects.ProjectFolder)
{
return options;
}
if (!_recurseIntoChildren)
{
options &= ~UxrComponentProcessingOptions.RecurseIntoChildren;
}
if (!_recurseIntoPrefabs)
{
options &= ~(UxrComponentProcessingOptions.RecurseIntoPrefabs);
}
return options;
}
}
private Dictionary<string, int> _prefabComponents = new Dictionary<string, int>();
private Dictionary<string, int> _sceneComponents = new Dictionary<string, int>();
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1feb84519bbf4b1428d388706c41ed59
timeCreated: 1626797079

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 28913d8f3a9c59642a358774451db386
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,175 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UniqueIdGenerationWindow.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Xml;
using UltimateXR.Core;
using UltimateXR.Core.Unique;
using UltimateXR.Extensions.Unity;
using UnityEditor;
using UnityEngine;
namespace UltimateXR.Editor.Utilities.MultiplayerUtils
{
/// <summary>
/// Custom tool window that will make sure that all UXR elements in the project have correct
/// <see cref="UniqueId" /> values.
/// </summary>
public sealed class UniqueIdGenerationWindow : ComponentProcessorWindow<Component>
{
#region Public Methods
/// <summary>
/// Fixes the given component Unique ID information if necessary.
/// </summary>
/// <param name="info">Information about the component</param>
/// <param name="forceRegenerateId">Whether to force the regeneration of the unique Id.</param>
/// <param name="onlyCheck">
/// Whether to only check, and return a value telling if the component requires changes, but not
/// perform any modifications on it
/// </param>
/// <returns>Whether the component required changes</returns>
public static bool FixComponentUniqueIdInformation(UxrComponentInfo<Component> info, bool forceRegenerateId, bool onlyCheck)
{
SerializedObject serializedObject = new SerializedObject(info.TargetComponent);
serializedObject.Update();
SerializedProperty uniqueIdProperty = serializedObject.FindProperty(UxrEditorUtils.PropertyUniqueId);
SerializedProperty prefabGuidProperty = serializedObject.FindProperty(UxrEditorUtils.PropertyPrefabGuid);
SerializedProperty isInPrefabProperty = serializedObject.FindProperty(UxrEditorUtils.PropertyIsInPrefab);
if (!info.TargetComponent.GetPrefabGuid(out string prefabGuid))
{
return false;
}
if (uniqueIdProperty == null || prefabGuidProperty == null || isInPrefabProperty == null)
{
Debug.LogError($"Can't find one or more fields in {info.TargetComponent}. Target type ({info.TargetComponent.GetType().FullName}) is missing one or more of these required fields: {UxrEditorUtils.PropertyUniqueId}, {UxrEditorUtils.PropertyPrefabGuid}, {UxrEditorUtils.PropertyIsInPrefab}.");
return false;
}
bool isInPrefab = info.TargetComponent.IsInPrefab();
bool needsChange = false;
if (prefabGuidProperty.stringValue != prefabGuid || (!info.IsOriginalSource && !prefabGuidProperty.prefabOverride))
{
if (!onlyCheck)
{
// By changing values this way, we ensure that the property is marked as overriden.
// We should be able to use prefabGuidProperty.prefabOverride = true, but it gives unknown errors in some cases.
prefabGuidProperty.stringValue = "AA";
serializedObject.ApplyModifiedProperties();
prefabGuidProperty.stringValue = prefabGuid;
}
needsChange = true;
}
if (isInPrefabProperty.boolValue != isInPrefab || (!info.IsOriginalSource && !isInPrefabProperty.prefabOverride))
{
if (!onlyCheck)
{
// By changing values this way, we ensure that the property is marked as overriden.
// We should be able to use isInPrefabProperty.prefabOverride = true, but it gives unknown errors in some cases.
isInPrefabProperty.boolValue = !isInPrefab;
serializedObject.ApplyModifiedProperties();
isInPrefabProperty.boolValue = isInPrefab;
}
needsChange = true;
}
if (forceRegenerateId || string.IsNullOrEmpty(uniqueIdProperty.stringValue) || (!info.IsOriginalSource && !uniqueIdProperty.prefabOverride))
{
if (!onlyCheck)
{
Guid newUniqueID = UxrUniqueIdImplementer.GetNewUniqueId();
uniqueIdProperty.stringValue = "AA";
serializedObject.ApplyModifiedProperties();
uniqueIdProperty.stringValue = newUniqueID.ToString();
}
needsChange = true;
}
if (!onlyCheck && needsChange)
{
serializedObject.ApplyModifiedProperties();
}
return needsChange;
}
#endregion
#region Event Trigger Methods
/// <inheritdoc />
protected override bool OnProcessStarting()
{
if (!base.OnProcessStarting())
{
return false;
}
EditorPrefs.SetBool(UxrConstants.Editor.AutomaticIdGenerationPrefs, false);
return true;
}
/// <inheritdoc />
protected override void OnProcessEnded()
{
base.OnProcessEnded();
EditorPrefs.SetBool(UxrConstants.Editor.AutomaticIdGenerationPrefs, true);
}
#endregion
#region Protected Overrides ComponentProcessorWindow<Component>
/// <inheritdoc />
protected override bool CanProcessUltimateXRAssets => true;
/// <inheritdoc />
protected override string HelpBoxMessage => "This generates missing unique IDs for UltimateXR components. Unique IDs are used to identify objects so that functionality such as multi-user and state saves can work correctly. UltimateXR versions prior to 1.0.0 might have missing IDs since unique ID information was first introduced in 1.0.0. Use this tool to generate missing unique ID information in projects created with older versions.";
/// <inheritdoc />
protected override string DontIgnoreUxrAssetsWarningMessage => "All assets in UltimateXR come already with unique ID information. Changing it may have unwanted results";
/// <inheritdoc />
protected override string ProcessButtonText => "Generate";
/// <inheritdoc />
protected override void ProcessComponent(UxrComponentInfo<Component> info, bool onlyCheck, out bool isChanged, out bool forceNoLog)
{
isChanged = false;
forceNoLog = true;
if (info.TargetComponent is IUxrUniqueId)
{
forceNoLog = false;
isChanged = FixComponentUniqueIdInformation(info, false, onlyCheck);
}
}
#endregion
#region Private Methods
/// <summary>
/// Menu entry that invokes the tool.
/// </summary>
[MenuItem(UxrConstants.Editor.MenuPathNetworking + "Check Unique IDs", priority = UxrConstants.Editor.PriorityMenuPathNetworking + 1)]
private static void Init()
{
UniqueIdGenerationWindow window = (UniqueIdGenerationWindow)GetWindow(typeof(UniqueIdGenerationWindow));
window.Show();
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52656d5eee25967489341e5322fa4e2f
timeCreated: 1626797079

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 08417134c6d924a438c34e6734037fc5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AlignWindow.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UltimateXR.Extensions.Unity;
using UnityEditor;
using UnityEngine;
namespace UltimateXR.Editor.Utilities.TransformUtils
{
/// <summary>
/// Custom tool window that will reposition/reorient an object so that the source transform would match the target
/// transform.
/// </summary>
public class AlignWindow : EditorWindow
{
#region Unity
/// <summary>
/// Draws the inspector and gathers user input.
/// </summary>
private void OnGUI()
{
EditorGUILayout.HelpBox("This utility will reposition/reorient an object in a way so that it would make source match target", MessageType.Info);
EditorGUI.BeginChangeCheck();
Transform objectToAlign = _objectToAlign;
_objectToAlign = EditorGUILayout.ObjectField(new GUIContent("Object To Align", ""), _objectToAlign, typeof(Transform), true) as Transform;
if (EditorGUI.EndChangeCheck())
{
if (EditorUtility.IsPersistent(_objectToAlign))
{
_objectToAlign = objectToAlign;
EditorUtility.DisplayDialog(UxrConstants.Editor.Error, "The object to align needs to be in the scene", UxrConstants.Editor.Ok);
}
}
_referenceSource = EditorGUILayout.ObjectField(new GUIContent("Reference Source", ""), _referenceSource, typeof(Transform), true) as Transform;
_referenceTarget = EditorGUILayout.ObjectField(new GUIContent("Reference Target", ""), _referenceTarget, typeof(Transform), true) as Transform;
_reposition = EditorGUILayout.Toggle(new GUIContent("Reposition", ""), _reposition);
_reorient = EditorGUILayout.Toggle(new GUIContent("Reorient", ""), _reorient);
GUI.enabled = _objectToAlign && _referenceSource && _referenceTarget;
if (UxrEditorUtils.CenteredButton(new GUIContent("Align")))
{
Undo.RegisterCompleteObjectUndo(_objectToAlign.transform, "Align object");
_objectToAlign.ApplyAlignment(_referenceSource, _referenceTarget, UxrUtils.BuildTransformations(_reposition, _reorient));
}
GUI.enabled = true;
}
#endregion
#region Private Methods
/// <summary>
/// Menu entry that invokes the tool.
/// </summary>
[MenuItem(UxrConstants.Editor.MenuPathUtils + "Align Object", priority = UxrConstants.Editor.PriorityMenuPathUtils + 1)]
private static void Init()
{
AlignWindow window = (AlignWindow)GetWindow(typeof(AlignWindow), true, "Align Object");
window.Show();
}
#endregion
#region Private Types & Data
private Transform _objectToAlign;
private Transform _referenceSource;
private Transform _referenceTarget;
private bool _reposition = true;
private bool _reorient = true;
#endregion
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: ae6cd2a1c2ad2aa49adb72d0073c05d3
timeCreated: 1516916387
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LookAtWindow.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UnityEditor;
using UnityEngine;
namespace UltimateXR.Editor.Utilities.TransformUtils
{
/// <summary>
/// Custom tool window to implement a LookAt on an object transform.
/// </summary>
public class LookAtWindow : EditorWindow
{
#region Unity
/// <summary>
/// Draws the inspector and gathers user input.
/// </summary>
private void OnGUI()
{
EditorGUILayout.HelpBox("This utility will make an object face a target. The look-at direction will be applied to the object's forward vector", MessageType.Info);
EditorGUI.BeginChangeCheck();
Transform objectToLookAt = _object;
_object = EditorGUILayout.ObjectField(new GUIContent("Object", ""), _object, typeof(Transform), true) as Transform;
if (EditorGUI.EndChangeCheck())
{
if (EditorUtility.IsPersistent(_object))
{
_object = objectToLookAt;
EditorUtility.DisplayDialog(UxrConstants.Editor.Error, "The object to process needs to be in the scene", UxrConstants.Editor.Ok);
}
}
_target = EditorGUILayout.ObjectField(new GUIContent("Target", ""), _target, typeof(Transform), true) as Transform;
_invertForward = EditorGUILayout.Toggle(new GUIContent("Invert Forward"), _invertForward);
GUI.enabled = _object && _target;
if (UxrEditorUtils.CenteredButton(new GUIContent("Look At")))
{
Undo.RegisterCompleteObjectUndo(_object.transform, "Look at object");
Vector3 forward = _target.position - _object.position;
_object.rotation = Quaternion.LookRotation(_invertForward ? -forward : forward);
}
GUI.enabled = true;
}
#endregion
#region Private Methods
/// <summary>
/// Menu entry that invokes the tool.
/// </summary>
[MenuItem(UxrConstants.Editor.MenuPathUtils + "LookAt Object", priority = UxrConstants.Editor.PriorityMenuPathUtils + 2)]
private static void Init()
{
LookAtWindow window = (LookAtWindow)GetWindow(typeof(LookAtWindow), true, "LookAt Object");
window.Show();
}
#endregion
#region Private Types & Data
private Transform _object;
private Transform _target;
private bool _invertForward;
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c9d5baecadf7402fa7b9ea82b71ec6fe
timeCreated: 1628846735

View File

@@ -0,0 +1,104 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MirrorWindow.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UltimateXR.Core.Math;
using UltimateXR.Editor.Core.Math;
using UltimateXR.Extensions.Unity;
using UnityEditor;
using UnityEngine;
namespace UltimateXR.Editor.Utilities.TransformUtils
{
/// <summary>
/// Custom tool window that will mirror an object's position/orientation with reference to another.
/// </summary>
public class MirrorWindow : EditorWindow
{
#region Unity
/// <summary>
/// Draws the inspector and gathers user input.
/// </summary>
private void OnGUI()
{
EditorGUILayout.HelpBox("This utility will mirror an object. The mirror position is defined by a transform and the mirror plane by the transform's forward vector.\nThe Mirror Type option controls which vectors from the object will be mirrored, the remaining one being computed using the cross-product of the other two.",
MessageType.Info);
EditorGUI.BeginChangeCheck();
UnityEngine.Transform objectToAlign = _objectToMirror;
_objectToMirror = EditorGUILayout.ObjectField(new GUIContent("Object to Mirror", "The object that will be mirrored"), _objectToMirror, typeof(UnityEngine.Transform), true) as UnityEngine.Transform;
if (EditorGUI.EndChangeCheck())
{
if (EditorUtility.IsPersistent(_objectToMirror))
{
_objectToMirror = objectToAlign;
EditorUtility.DisplayDialog(UxrConstants.Editor.Error, "The object to mirror needs to be in the scene", UxrConstants.Editor.Ok);
}
}
_useSelfSourceTransform = EditorGUILayout.Toggle(new GUIContent("Use Object As Source", "Whether to use the object to be mirrored as the source position/orientation"), _useSelfSourceTransform);
if (!_useSelfSourceTransform)
{
_sourceTransform = EditorGUILayout.ObjectField(new GUIContent("Source Reference", "The transform that will be used as reference for the start position/orientation"), _sourceTransform, typeof(UnityEngine.Transform), true) as UnityEngine.Transform;
}
_mirrorPlane = EditorGUILayout.ObjectField(new GUIContent("Mirror Plane", "A point where the mirror plane lies"), _mirrorPlane, typeof(UnityEngine.Transform), true) as UnityEngine.Transform;
_mirrorAxis = UxrAxisPropertyDrawer.EditorGuiLayout(new GUIContent("Mirror Axis", "The normal of the axis plane"), _mirrorAxis);
_reposition = EditorGUILayout.Toggle(new GUIContent("Reposition", "Change position?"), _reposition);
_reorient = EditorGUILayout.Toggle(new GUIContent("Reorient", "Change orientation?"), _reorient);
GUI.enabled = _reorient;
_mirrorType = (TransformExt.MirrorType)EditorGUILayout.EnumPopup("Mirror Type", _mirrorType);
GUI.enabled = _objectToMirror != null && _mirrorPlane != null;
if (UxrEditorUtils.CenteredButton(new GUIContent("Mirror")))
{
Undo.RegisterCompleteObjectUndo(_objectToMirror.transform, "Mirror object");
if (!_useSelfSourceTransform && _sourceTransform)
{
_objectToMirror.SetPositionAndRotation(_sourceTransform);
}
_objectToMirror.ApplyMirroring(_mirrorPlane, _mirrorAxis, _mirrorType, _reorient, _reposition);
}
GUI.enabled = true;
}
#endregion
#region Private Methods
/// <summary>
/// Menu entry that invokes the tool.
/// </summary>
[MenuItem(UxrConstants.Editor.MenuPathUtils + "Mirror Object", priority = UxrConstants.Editor.PriorityMenuPathUtils + 3)]
private static void Init()
{
MirrorWindow window = (MirrorWindow)GetWindow(typeof(MirrorWindow), true, "Mirror Object");
window.Show();
}
#endregion
#region Private Types & Data
private UnityEngine.Transform _objectToMirror;
private bool _useSelfSourceTransform = true;
private UnityEngine.Transform _sourceTransform;
private UnityEngine.Transform _mirrorPlane;
private UxrAxis _mirrorAxis = UxrAxis.Z;
private TransformExt.MirrorType _mirrorType = TransformExt.MirrorType.MirrorYZ;
private bool _reposition = true;
private bool _reorient = true;
#endregion
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 5cf17205ae70ea045bcfca2fefd80085
timeCreated: 1516916387
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrLogWindow.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace UltimateXR.Editor.Utilities
{
/// <summary>
/// Simple log window.
/// </summary>
public class UxrLogWindow : EditorWindow
{
#region Inspector Properties/Serialized Fields
[SerializeField] private List<string> _infoLines = new List<string>();
[SerializeField] private Vector2 _scrollPos;
#endregion
#region Public Types & Data
public const int DefaultWindowWidth = 1400;
public const int DefaultWindowHeight = 600;
#endregion
#region Public Methods
/// <summary>
/// Shows the window with the log.
/// </summary>
/// <param name="infoLines"></param>
public static void ShowLog(IEnumerable<string> infoLines, int windowWidth = DefaultWindowWidth, int windowHeight = DefaultWindowHeight)
{
UxrLogWindow window = (UxrLogWindow)GetWindow(typeof(UxrLogWindow));
int x = (Screen.currentResolution.width - windowWidth) / 2;
int y = (Screen.currentResolution.height - windowHeight) / 2;
window.position = new Rect(x, y, windowWidth, windowHeight);
window._infoLines = infoLines.ToList();
window.Show();
}
#endregion
#region Unity
/// <summary>
/// Draws the window.
/// </summary>
private void OnGUI()
{
EditorGUILayout.BeginVertical();
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, GUILayout.Width(position.width), GUILayout.Height(position.height - EditorGUIUtility.singleLineHeight * 2));
foreach (string line in _infoLines)
{
GUILayout.Label(line);
}
EditorGUILayout.EndScrollView();
if (UxrEditorUtils.CenteredButton(new GUIContent("Close")))
{
Close();
}
EditorGUILayout.EndVertical();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c939d9dbb65d74d4abe2e7de3767f5be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: