Add ultimate xr
This commit is contained in:
@@ -0,0 +1,397 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="GameObjectExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Extensions.System;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UltimateXR.Manipulation;
|
||||
using UltimateXR.UI.UnityInputModule;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
#endif
|
||||
|
||||
namespace UltimateXR.Extensions.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="GameObject" /> extensions.
|
||||
/// </summary>
|
||||
public static class GameObjectExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Component of a given type in the GameObject or any of its parents. It also works on prefabs, where regular
|
||||
/// <see cref="Component.GetComponentInParent" /> will not work:
|
||||
/// https://issuetracker.unity3d.com/issues/getcomponentinparent-is-returning-null-when-the-gameobject-is-a-prefab
|
||||
/// It also works on objects recently instantiated or on disabled objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type to get</typeparam>
|
||||
/// <returns>Component in same GameObject or any of its parents. Null if it wasn't found</returns>
|
||||
public static T SafeGetComponentInParent<T>(this GameObject self)
|
||||
{
|
||||
T parent = self.GetComponentInParent<T>();
|
||||
return parent ?? self.GetComponentsInParent<T>(true).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates/deactivates the object if it isn't active already.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to activate</param>
|
||||
/// <param name="activate">Whether to activate or deactivate the object</param>
|
||||
public static void CheckSetActive(this GameObject self, bool activate)
|
||||
{
|
||||
if (self.activeSelf != activate)
|
||||
{
|
||||
self.SetActive(activate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique path in the scene for the given GameObject. It will include sibling indices to make it unique.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to get the unique path for</param>
|
||||
/// <returns>Unique GameObject path string</returns>
|
||||
/// <seealso cref="TransformExt.GetUniqueScenePath" />
|
||||
public static string GetUniqueScenePath(this GameObject self)
|
||||
{
|
||||
self.ThrowIfNull(nameof(self));
|
||||
return self.transform.GetUniqueScenePath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path in the scene of the given GameObject.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to get the scene path for</param>
|
||||
/// <returns>Path of the GameObject in the scene</returns>
|
||||
/// <seealso cref="TransformExt.GetPathUnderScene" />
|
||||
public static string GetPathUnderScene(this GameObject self)
|
||||
{
|
||||
self.ThrowIfNull(nameof(self));
|
||||
return self.transform.GetPathUnderScene();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given GameObject is the root GameObject inside a prefab.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to check</param>
|
||||
/// <returns>Whether the GameObject is the root GameObject inside a prefab</returns>
|
||||
public static bool IsPrefabRoot(this GameObject self)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (PrefabStageUtility.GetCurrentPrefabStage() != null && PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot == self)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return self.scene.name == null && self.transform.parent == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given GameObject is located inside a prefab.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to check</param>
|
||||
/// <returns>Whether the GameObject is located inside a prefab</returns>
|
||||
public static bool IsInPrefab(this GameObject self)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
if (PrefabStageUtility.GetCurrentPrefabStage() != null && PrefabStageUtility.GetCurrentPrefabStage().IsPartOfPrefabContents(self))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return PrefabUtility.IsPartOfPrefabAsset(self);
|
||||
#else
|
||||
return self.scene.name == null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID of the prefab the GameObject is in, if it is in a prefab, or the GUID of the prefab the GameObject
|
||||
/// was instantiated from, if it was instantiated from a prefab. If it is not
|
||||
/// </summary>
|
||||
/// <param name="self">The GameObject to retrieve the information of</param>
|
||||
/// <param name="prefabGuid">If the call was successful, returns the GUID or string.Empty</param>
|
||||
/// <param name="assetPath">If the call was successful, returns the asset path or string.Empty</param>
|
||||
/// <returns>Whether the call was successful</returns>
|
||||
/// <remarks>The reason the call can be unsuccessful is because Unity for some reason will report
|
||||
/// a null/empty asset path even though PrefabUtility.IsPartOfPrefabAsset() returns true.
|
||||
/// This behaviour happens when in prefab isolation/context mode in the editor</remarks>
|
||||
public static bool GetPrefabGuid(this GameObject self, out string prefabGuid, out string assetPath)
|
||||
{
|
||||
prefabGuid = string.Empty;
|
||||
assetPath = string.Empty;
|
||||
|
||||
if (PrefabStageUtility.GetCurrentPrefabStage() != null && PrefabStageUtility.GetCurrentPrefabStage().IsPartOfPrefabContents(self))
|
||||
{
|
||||
// Asset in prefab view
|
||||
assetPath = PrefabStageUtility.GetCurrentPrefabStage().assetPath;
|
||||
}
|
||||
else if (PrefabUtility.IsPartOfPrefabAsset(self))
|
||||
{
|
||||
// Prefab asset
|
||||
assetPath = AssetDatabase.GetAssetPath(self);
|
||||
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (PrefabUtility.IsPartOfNonAssetPrefabInstance(self) && self.scene.name != null)
|
||||
{
|
||||
GameObject instanceRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(self);
|
||||
GameObject rootPrefab = PrefabUtility.GetCorrespondingObjectFromSource(instanceRoot);
|
||||
assetPath = AssetDatabase.GetAssetPath(rootPrefab);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
// Try to get GUID from asset path, checking if path is valid. Otherwise return null.
|
||||
|
||||
// Check if all zeroes, which means invalid guid.
|
||||
|
||||
prefabGuid = AssetDatabase.GUIDFromAssetPath(assetPath).ToString();
|
||||
prefabGuid = string.IsNullOrEmpty(prefabGuid) || prefabGuid.All(c => c == '0') ? null : prefabGuid;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given GameObject is dynamic. Since <see cref="GameObject.isStatic" /> doesn't work at runtime
|
||||
/// due to the static flags being editor-only, a workaround is required to try to find out if an object is dynamic or
|
||||
/// not.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to check</param>
|
||||
/// <returns>Whether the object appears to be dynamic</returns>
|
||||
public static bool IsDynamic(this GameObject self)
|
||||
{
|
||||
return self.GetComponentInParent<Rigidbody>() != null ||
|
||||
self.GetComponentInParent<SkinnedMeshRenderer>() != null ||
|
||||
self.GetComponentInParent<UxrAvatar>() != null ||
|
||||
self.GetComponentInParent<UxrGrabbableObject>() != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Component of a given type. If it doesn't exist, it is added to the GameObject.
|
||||
/// </summary>
|
||||
/// <param name="self">Target GameObject where the component will be looked for and added to if it doesn't exist</param>
|
||||
/// <typeparam name="T">Component type to get or add</typeparam>
|
||||
/// <returns>Existing component or newly added if it didn't exist before</returns>
|
||||
public static T GetOrAddComponent<T>(this GameObject self) where T : Component
|
||||
{
|
||||
T component = self.GetComponent<T>();
|
||||
|
||||
if (component == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
component = self.AddComponent<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
component = Undo.AddComponent<T>(self);
|
||||
}
|
||||
#else
|
||||
component = self.AddComponent<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GameObject in the exact same position as the given one and parents it.
|
||||
/// </summary>
|
||||
/// <param name="parent">
|
||||
/// GameObject to parent the new object to and also place it at the same position
|
||||
/// and with the same orientation
|
||||
/// </param>
|
||||
/// <param name="newGameObjectName">Name for the new GameObject</param>
|
||||
/// <returns>New created GameObject</returns>
|
||||
public static GameObject CreateGameObjectAndParentSameTransform(GameObject parent, string newGameObjectName)
|
||||
{
|
||||
GameObject newGameObject = new GameObject(newGameObjectName);
|
||||
newGameObject.transform.parent = parent.transform;
|
||||
newGameObject.transform.localPosition = Vector3.zero;
|
||||
newGameObject.transform.localRotation = Quaternion.identity;
|
||||
return newGameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the geometric center of the given GameObject based on all the MeshRenderers in the hierarchy.
|
||||
/// </summary>
|
||||
/// <param name="self">GameObject to compute the geometric center of</param>
|
||||
/// <returns>Geometric center</returns>
|
||||
public static Vector3 GetGeometricCenter(this GameObject self)
|
||||
{
|
||||
IEnumerable<MeshRenderer> meshRenderers = self.GetComponentsInChildren<MeshRenderer>().Where(r => !r.hideFlags.HasFlag(HideFlags.HideInHierarchy) && r.enabled);
|
||||
bool initialized = false;
|
||||
Vector3 min = Vector3.zero;
|
||||
Vector3 max = Vector3.zero;
|
||||
|
||||
if (!meshRenderers.Any())
|
||||
{
|
||||
return self.transform.position;
|
||||
}
|
||||
|
||||
foreach (MeshRenderer renderer in meshRenderers)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = true;
|
||||
min = renderer.bounds.min;
|
||||
max = renderer.bounds.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
min = Vector3.Min(min, renderer.bounds.min);
|
||||
max = Vector3.Max(max, renderer.bounds.max);
|
||||
}
|
||||
}
|
||||
|
||||
return (min + max) * 0.5f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="GameObject" /> <see cref="Bounds" />. The bounds are the <see cref="Renderer" />'s bounds
|
||||
/// if there is one in the GameObject. Otherwise it will encapsulate all renderers found in the children.
|
||||
/// If <paramref name="forceRecurseIntoChildren" /> is true, it will also encapsulate all renderers found in
|
||||
/// the children no matter if the GameObject has a Renderer component or not.
|
||||
/// </summary>
|
||||
/// <param name="self">The GameObject whose <see cref="Bounds" /> to get</param>
|
||||
/// <param name="forceRecurseIntoChildren">
|
||||
/// Whether to also encapsulate all renderers found in the children no matter if the
|
||||
/// GameObject has a Renderer component or not
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <see cref="Bounds" /> in world-space.
|
||||
/// </returns>
|
||||
public static Bounds GetBounds(this GameObject self, bool forceRecurseIntoChildren)
|
||||
{
|
||||
Renderer renderer = self.GetComponent<Renderer>();
|
||||
|
||||
if (renderer != null && !forceRecurseIntoChildren)
|
||||
{
|
||||
if (renderer.enabled)
|
||||
{
|
||||
return renderer.bounds;
|
||||
}
|
||||
|
||||
return new Bounds(self.transform.position, Vector3.zero);
|
||||
}
|
||||
|
||||
IEnumerable<Renderer> renderers = self.GetComponentsInChildren<Renderer>().Where(r => !r.hideFlags.HasFlag(HideFlags.HideInHierarchy) && r.enabled);
|
||||
|
||||
if (!renderers.Any())
|
||||
{
|
||||
return new Bounds(self.transform.position, Vector3.zero);
|
||||
}
|
||||
|
||||
Vector3 min = Vector3Ext.Min(renderers.Select(r => r.bounds.min));
|
||||
Vector3 max = Vector3Ext.Max(renderers.Select(r => r.bounds.max));
|
||||
|
||||
return new Bounds((max + min) * 0.5f, max - min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="GameObject" /> <see cref="Bounds" /> in local space. The bounds are the
|
||||
/// <see cref="Renderer" />'s bounds if there is one in the GameObject. Otherwise it will encapsulate all renderers
|
||||
/// found in the children.
|
||||
/// If <paramref name="forceRecurseIntoChildren" /> is true, it will also encapsulate all renderers found in
|
||||
/// the children no matter if the GameObject has a Renderer component or not.
|
||||
/// </summary>
|
||||
/// <param name="self">The GameObject whose local <see cref="Bounds" /> to get</param>
|
||||
/// <param name="forceRecurseIntoChildren">
|
||||
/// Whether to also encapsulate all renderers found in the children no matter if the
|
||||
/// GameObject has a Renderer component or not
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Local <see cref="Bounds" />.
|
||||
/// </returns>
|
||||
public static Bounds GetLocalBounds(this GameObject self, bool forceRecurseIntoChildren)
|
||||
{
|
||||
Renderer renderer = self.GetComponent<Renderer>();
|
||||
|
||||
if (renderer != null && !forceRecurseIntoChildren)
|
||||
{
|
||||
if (renderer.enabled)
|
||||
{
|
||||
return renderer.localBounds;
|
||||
}
|
||||
|
||||
return new Bounds();
|
||||
}
|
||||
|
||||
IEnumerable<Renderer> renderers = self.GetComponentsInChildren<Renderer>().Where(r => !r.hideFlags.HasFlag(HideFlags.HideInHierarchy) && r.enabled);
|
||||
|
||||
if (!renderers.Any())
|
||||
{
|
||||
return new Bounds();
|
||||
}
|
||||
|
||||
IEnumerable<Vector3> allMinMaxToLocal = renderers.Select(r => self.transform.InverseTransformPoint(r.transform.TransformPoint(r.localBounds.min))).Concat(renderers.Select(r => self.transform.InverseTransformPoint(r.transform.TransformPoint(r.localBounds.max))));
|
||||
Vector3 min = Vector3Ext.Min(allMinMaxToLocal);
|
||||
Vector3 max = Vector3Ext.Max(allMinMaxToLocal);
|
||||
|
||||
return new Bounds((max + min) * 0.5f, max - min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the layer of a GameObject and all its children.
|
||||
/// </summary>
|
||||
/// <param name="self">The root GameObject from where to start</param>
|
||||
/// <param name="layer">The layer value to assign</param>
|
||||
public static void SetLayerRecursively(this GameObject self, int layer)
|
||||
{
|
||||
if (self != null)
|
||||
{
|
||||
Transform selfTransform = self.transform;
|
||||
|
||||
self.gameObject.layer = layer;
|
||||
|
||||
for (int i = 0; i < selfTransform.childCount; ++i)
|
||||
{
|
||||
SetLayerRecursively(selfTransform.GetChild(i).gameObject, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given GameObject's layer is present in a layer mask.
|
||||
/// </summary>
|
||||
/// <param name="self">The GameObject whose layer to check</param>
|
||||
/// <param name="layerMask">The layer mask to check against</param>
|
||||
/// <returns>Whether the GameObject's layer is present in the layer mask</returns>
|
||||
public static bool IsInLayerMask(this GameObject self, LayerMask layerMask)
|
||||
{
|
||||
return (1 << self.layer & layerMask.value) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the topmost <see cref="UxrCanvas" /> upwards in the hierarchy if it exists.
|
||||
/// </summary>
|
||||
/// <param name="self">The GameObject whose parents to look for</param>
|
||||
/// <returns>The topmost <see cref="UxrCanvas" /> component upwards in the hierarchy or null if it doesn't exists</returns>
|
||||
public static UxrCanvas GetTopmostCanvas(this GameObject self)
|
||||
{
|
||||
UxrCanvas[] canvases = self.GetComponentsInParent<UxrCanvas>();
|
||||
return ComponentExt.GetCommonRootComponentFromSet(canvases);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user