Commit 29de0c28 authored by BlackAngle233's avatar BlackAngle233
Browse files

10.19 learned

parent 912976bb
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods and helper functions for various math data
/// </summary>
public static class MathExtensions
{
public static int MostSignificantBit(this int x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x & ~(x >> 1);
}
public static int PowerOfTwoGreaterThanOrEqualTo(this int v)
{
return Mathf.IsPowerOfTwo(v) ? v : Mathf.NextPowerOfTwo(v);
}
public static Vector3Int PowerOfTwoGreaterThanOrEqualTo(this Vector3Int v)
{
return new Vector3Int(PowerOfTwoGreaterThanOrEqualTo(v.x),
PowerOfTwoGreaterThanOrEqualTo(v.y),
PowerOfTwoGreaterThanOrEqualTo(v.z));
}
public static int CubicToLinearIndex(Vector3Int ndx, Vector3Int size)
{
return (ndx.x) +
(ndx.y * size.x) +
(ndx.z * size.x * size.y);
}
public static Vector3Int LinearToCubicIndex(int linearIndex, Vector3Int size)
{
return new Vector3Int((linearIndex / 1) % size.x,
(linearIndex / size.x) % size.y,
(linearIndex / (size.x * size.y)) % size.z);
}
public static Vector3 ClampComponentWise(Vector3 value, Vector3 min, Vector3 max)
{
return new Vector3(Mathf.Clamp(value.x, min.x, max.x),
Mathf.Clamp(value.y, min.y, max.y),
Mathf.Clamp(value.z, min.z, max.z));
}
/// <summary>
/// Sets the value to zero if greater than the specified amount.
/// </summary>
public static int ResetIfGreaterThan(this int value, int amount)
{
if (value > amount)
{
value = 0;
}
return value;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#if UNITY_EDITOR || !UNITY_WSA
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.MixedReality.Toolkit.Utilities;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Process Extension class.
/// </summary>
public static class ProcessExtensions
{
/// <summary>
/// Starts a process asynchronously.
/// </summary>
/// <param name="process">This Process.</param>
/// <param name="fileName">The process executable to run.</param>
/// <param name="args">The Process arguments.</param>
/// <param name="showDebug">Should output debug code to Editor Console?</param>
/// <returns><see cref="Utilities.ProcessResult"/></returns>
public static async Task<ProcessResult> StartProcessAsync(this Process process, string fileName, string args, bool showDebug = false, CancellationToken cancellationToken = default)
{
return await StartProcessAsync(process, new ProcessStartInfo
{
FileName = fileName,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = args
}, showDebug, cancellationToken);
}
/// <summary>
/// Starts a process asynchronously.<para/>
/// </summary>
/// <remarks>The provided Process Start Info must not use shell execution, and should redirect the standard output and errors.</remarks>
/// <param name="process">This Process.</param>
/// <param name="startInfo">The Process start info.</param>
/// <param name="showDebug">Should output debug code to Editor Console?</param>
/// <returns><see cref="Utilities.ProcessResult"/></returns>
public static async Task<ProcessResult> StartProcessAsync(this Process process, ProcessStartInfo startInfo, bool showDebug = false, CancellationToken cancellationToken = default)
{
Debug.Assert(!startInfo.UseShellExecute, "Process Start Info must not use shell execution.");
Debug.Assert(startInfo.RedirectStandardOutput, "Process Start Info must redirect standard output.");
Debug.Assert(startInfo.RedirectStandardError, "Process Start Info must redirect standard errors.");
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
var processResult = new TaskCompletionSource<ProcessResult>();
var errorCodeResult = new TaskCompletionSource<string[]>();
var errorList = new List<string>();
var outputCodeResult = new TaskCompletionSource<string[]>();
var outputList = new List<string>();
process.Exited += OnProcessExited;
process.ErrorDataReceived += OnErrorDataReceived;
process.OutputDataReceived += OnOutputDataReceived;
async void OnProcessExited(object sender, EventArgs args)
{
processResult.TrySetResult(new ProcessResult(process.ExitCode, await errorCodeResult.Task, await outputCodeResult.Task));
process.Close();
process.Dispose();
}
void OnErrorDataReceived(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
errorList.Add(args.Data);
if (!showDebug)
{
return;
}
UnityEngine.Debug.LogError(args.Data);
}
else
{
errorCodeResult.TrySetResult(errorList.ToArray());
}
}
void OnOutputDataReceived(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
outputList.Add(args.Data);
if (!showDebug)
{
return;
}
UnityEngine.Debug.Log(args.Data);
}
else
{
outputCodeResult.TrySetResult(outputList.ToArray());
}
}
if (!process.Start())
{
if (showDebug)
{
UnityEngine.Debug.LogError("Failed to start process!");
}
processResult.TrySetResult(new ProcessResult(process.ExitCode, new[] { "Failed to start process!" }, null));
}
else
{
process.BeginOutputReadLine();
process.BeginErrorReadLine();
CancellationWatcher(process);
}
async void CancellationWatcher(Process _process)
{
await Task.Run(() =>
{
try
{
while (!_process.HasExited)
{
if (cancellationToken.IsCancellationRequested)
{
_process.Kill();
}
}
}
catch
{
// ignored
}
});
}
return await processResult.Task;
}
}
}
#endif
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Quaternion struct.
/// </summary>
public static class QuaternionExtensions
{
public static bool IsValidRotation(this Quaternion rotation)
{
return !float.IsNaN(rotation.x) && !float.IsNaN(rotation.y) && !float.IsNaN(rotation.z) && !float.IsNaN(rotation.w) &&
!float.IsInfinity(rotation.x) && !float.IsInfinity(rotation.y) && !float.IsInfinity(rotation.z) && !float.IsInfinity(rotation.w);
}
/// <summary>
/// Determines if the angle between two quaternions is within a given tolerance.
/// </summary>
/// <param name="q1">The first quaternion.</param>
/// <param name="q2">The second quaternion.</param>
/// <param name="angleTolerance">The maximum angle that will cause this to return true.</param>
/// <returns>True if the quaternions are aligned within the tolerance, false otherwise.</returns>
public static bool AlignedEnough(Quaternion q1, Quaternion q2, float angleTolerance)
{
return Mathf.Abs(Quaternion.Angle(q1, q2)) < angleTolerance;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Ray struct
/// </summary>
public static class RayExtensions
{
/// <summary>
/// Determines whether or not a ray is valid.
/// </summary>
/// <param name="ray">The ray being tested.</param>
/// <returns>True if the ray is valid, false otherwise.</returns>
public static bool IsValid(this Ray ray)
{
return ray.direction != Vector3.zero;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#if WINDOWS_UWP && !ENABLE_IL2CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for .Net reflection functions
/// </summary>
public static class ReflectionExtensions
{
public static EventInfo GetEvent(this Type type, string eventName)
{
return type.GetRuntimeEvent(eventName);
}
public static MethodInfo GetMethod(this Type type, string methodName)
{
return GetMethod(type, methodName, (BindingFlags)0x0);
}
public static MethodInfo GetMethod(this Type type, string methodName, BindingFlags flags)
{
var result = type.GetTypeInfo().GetDeclaredMethod(methodName);
if (((flags & BindingFlags.FlattenHierarchy) != 0) && result == null)
{
var baseType = type.GetTypeInfo().BaseType;
if (baseType != null)
{
return GetMethod(baseType, methodName, flags);
}
}
return result;
}
public static MethodInfo GetMethod(this Type type, string methodName, BindingFlags bindingAttr, Object binder, Type[] parameters, Object[] modifiers)
{
var result = type.GetTypeInfo().GetDeclaredMethod(methodName);
if (result == null)
{
var baseType = type.GetTypeInfo().BaseType;
if (baseType != null)
{
return GetMethod(baseType, methodName, bindingAttr, binder, parameters, modifiers);
}
}
return result;
}
public static MethodInfo GetMethod(this Type type, string methodName, Type[] parameters)
{
return GetMethods(type).Where(m => m.Name == methodName).FirstOrDefault(
m =>
{
var types = m.GetParameters().Select(p => p.ParameterType).ToArray();
if (types.Length == parameters.Length)
{
for (int idx = 0; idx < types.Length; idx++)
{
if (types[idx] != parameters[idx])
{
return false;
}
}
return true;
}
else
{
return false;
}
}
);
}
public static IEnumerable<MethodInfo> GetMethods(this Type type)
{
return GetMethods(type, (BindingFlags)0x0);
}
public static IEnumerable<MethodInfo> GetMethods(this Type type, BindingFlags flags)
{
return type.GetTypeInfo().GetMethods(flags);
}
public static IEnumerable<MethodInfo> GetMethods(this TypeInfo type)
{
return GetMethods(type, (BindingFlags)0x0);
}
public static IEnumerable<MethodInfo> GetMethods(this TypeInfo type, BindingFlags flags)
{
return type.DeclaredMethods;
}
public static IEnumerable<FieldInfo> GetFields(this Type type)
{
return GetFields(type, (BindingFlags)0x0);
}
public static IEnumerable<FieldInfo> GetFields(this Type type, BindingFlags flags)
{
return type.GetTypeInfo().DeclaredFields;
}
public static FieldInfo GetField(this Type type, string fieldName)
{
return type.GetRuntimeField(fieldName);
}
public static IEnumerable<PropertyInfo> GetProperties(this Type type, BindingFlags flags)
{
return type.GetTypeInfo().DeclaredProperties;
}
public static PropertyInfo GetProperty(this Type type, string propertyName)
{
return GetProperty(type, propertyName, (BindingFlags)0x0);
}
public static PropertyInfo GetProperty(this Type type, string propertyName, BindingFlags flags)
{
return type.GetRuntimeProperty(propertyName);
}
public static PropertyInfo GetProperty(this Type type, string propertyName, Type returnType)
{
return type.GetRuntimeProperty(propertyName);
}
public static IEnumerable<TypeInfo> GetTypeInfos(this Assembly assembly)
{
return assembly.DefinedTypes;
}
public static Type[] GetInterfaces(this Type type)
{
return type.GetTypeInfo().ImplementedInterfaces.ToArray();
}
public static bool IsClass(this Type type)
{
return type.GetTypeInfo().IsClass;
}
public static bool IsInterface(this Type type)
{
return type.GetTypeInfo().IsInterface;
}
public static bool IsAbstract(this Type type)
{
return type.GetTypeInfo().IsAbstract;
}
public static bool IsSubclassOf(this Type type, Type c)
{
return type.GetTypeInfo().IsSubclassOf(c);
}
public static bool IsAssignableFrom(this Type type, Type c)
{
return type.IsAssignableFrom(c.GetTypeInfo());
}
public static bool IsEnum(this Type type)
{
return type.GetTypeInfo().IsEnum;
}
public static bool IsValueType(this Type type)
{
return type.GetTypeInfo().IsValueType;
}
public static bool IsAssignableFrom(this Type type, TypeInfo typeInfo)
{
return type.GetTypeInfo().IsAssignableFrom(typeInfo);
}
public static object[] GetCustomAttributes(this Type type, bool inherit)
{
return type.GetTypeInfo().GetCustomAttributes(inherit).ToArray();
}
public static object[] GetCustomAttributes(this Type type, Type attributeType, bool inherit)
{
return type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).ToArray();
}
}
}
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Text;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// <see cref="System.Text.StringBuilder"/> Extensions.
/// </summary>
public static class StringBuilderExtensions
{
/// <summary>
/// Append new line for current Environment to this StringBuilder buffer
/// </summary>
public static StringBuilder AppendNewLine(this StringBuilder sb)
{
sb.Append(Environment.NewLine);
return sb;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.IO;
using System.Text;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// <see cref="System.String"/> Extensions.
/// </summary>
public static class StringExtensions
{
/// <summary>
/// Encodes the string to base 64 ASCII.
/// </summary>
/// <param name="toEncode">String to encode.</param>
/// <returns>Encoded string.</returns>
public static string EncodeTo64(this string toEncode)
{
byte[] toEncodeAsBytes = Encoding.ASCII.GetBytes(toEncode);
return Convert.ToBase64String(toEncodeAsBytes);
}
/// <summary>
/// Decodes string from base 64 ASCII.
/// </summary>
/// <param name="encodedData">String to decode.</param>
/// <returns>Decoded string.</returns>
public static string DecodeFrom64(this string encodedData)
{
byte[] encodedDataAsBytes = Convert.FromBase64String(encodedData);
return Encoding.ASCII.GetString(encodedDataAsBytes);
}
/// <summary>
/// Capitalize the first character and add a space before
/// each capitalized letter (except the first character).
/// </summary>
public static string ToProperCase(this string value)
{
// If there are 0 or 1 characters, just return the string.
if (value == null) { return value; }
if (value.Length < 2) { return value.ToUpper(); }
// If there's already spaces in the string, return.
if (value.Contains(" ")) { return value; }
// Start with the first character.
string result = value.Substring(0, 1).ToUpper();
// Add the remaining characters.
for (int i = 1; i < value.Length; i++)
{
if (char.IsLetter(value[i]) &&
char.IsUpper(value[i]))
{
// Add a space if the previous character is not upper-case.
// e.g. "LeftHand" -> "Left Hand"
if (i != 1 && // First character is upper-case in result.
(!char.IsLetter(value[i - 1]) || char.IsLower(value[i - 1])))
{
result += " ";
}
// If previous character is upper-case, only add space if the next
// character is lower-case. Otherwise assume this character to be inside
// an acronym.
// e.g. "OpenVRLeftHand" -> "Open VR Left Hand"
else if (i < value.Length - 1 &&
char.IsLetter(value[i + 1]) && char.IsLower(value[i + 1]))
{
result += " ";
}
}
result += value[i];
}
return result;
}
/// <summary>
/// Ensures directory separator chars in provided string are platform independent. Given path might use \ or / but not all platforms support both.
/// </summary>
public static string NormalizeSeparators(this string path)
=> path?.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
}
}
// Copyright(c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit
{
public static class SystemNumericsExtensions
{
/// <summary>
/// Converts this System.Numerics Vector3 to a UnityEngine Vector3.
/// </summary>
/// <param name="vector">The vector to convert.</param>
/// <returns>The converted vector.</returns>
public static UnityEngine.Vector3 ToUnityVector3(this System.Numerics.Vector3 vector)
{
return new UnityEngine.Vector3(vector.X, vector.Y, -vector.Z);
}
/// <summary>
/// Converts this System.Numerics Vector3 to a UnityEngine Vector3 format, storing values directly in referenced parameter
/// </summary>
public static void ConvertToUnityVector3(this System.Numerics.Vector3 source, ref UnityEngine.Vector3 target)
{
target.x = source.X;
target.y = source.Y;
target.z = -source.Z;
}
/// <summary>
/// Converts this System.Numerics Quaternion to a UnityEngine Quaternion.
/// </summary>
/// <param name="quaternion">The quaternion to convert.</param>
/// <returns>The converted quaternion.</returns>
public static UnityEngine.Quaternion ToUnityQuaternion(this System.Numerics.Quaternion quaternion)
{
return new UnityEngine.Quaternion(-quaternion.X, -quaternion.Y, quaternion.Z, quaternion.W);
}
/// <summary>
/// Converts this System.Numerics Quaternion to a UnityEngine Quaternion, storing values directly in referenced parameter
/// </summary>
public static void ConvertToUnityQuaternion(this System.Numerics.Quaternion source, ref UnityEngine.Quaternion target)
{
target.x = -source.X;
target.y = -source.Y;
target.z = source.Z;
target.w = source.W;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A collection of helper functions for Texture2D
/// </summary>
public static class Texture2DExtensions
{
/// <summary>
/// Fills the pixels. You will need to call Apply on the texture in order for changes to take place.
/// </summary>
/// <param name="texture2D">The Texture2D.</param>
/// <param name="row">The row to start filling at.</param>
/// <param name="col">The column to start filling at.</param>
/// <param name="width">The width to fill.</param>
/// <param name="height">The height to fill.</param>
/// <param name="fillColor">Color of the fill.</param>
/// <remarks>This function considers row 0 to be left and col 0 to be top.</remarks>
public static void FillPixels(this Texture2D texture2D, int row, int col, int width, int height, Color fillColor)
{
if (col + width > texture2D.width || row + height > texture2D.height)
{
throw new IndexOutOfRangeException();
}
Color32 toColor = fillColor; // Implicit cast
Color32[] colors = new Color32[width * height];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = toColor;
}
texture2D.SetPixels32(col, (texture2D.height - row) - height, width, height, colors);
}
/// <summary>
/// Fills the texture with a single color.
/// </summary>
/// <param name="texture2D">The Texture2D.</param>
/// <param name="fillColor">Color of the fill.</param>
public static void FillPixels(this Texture2D texture2D, Color fillColor)
{
texture2D.FillPixels(0, 0, texture2D.width, texture2D.height, fillColor);
}
/// <summary>
/// Captures a region of the screen and returns it as a texture.
/// </summary>
/// <param name="x">x position of the screen to capture from (bottom-left)</param>
/// <param name="y">y position of the screen to capture from (bottom-left)</param>
/// <param name="width">width of the screen area to capture</param>
/// <param name="height">height of the screen area to capture</param>
/// <returns>A Texture2D containing pixels from the region of the screen specified</returns>
/// <remarks>You should call this in OnPostRender.</remarks>
public static Texture2D CaptureScreenRegion(int x, int y, int width, int height)
{
Texture2D tex = new Texture2D(width, height);
tex.ReadPixels(new Rect(x, y, Screen.width, Screen.height), 0, 0);
tex.Apply();
return tex;
}
/// <summary>
/// Creates a texture from a defined region.
/// </summary>
/// <param name="texture2D">The Texture2D.</param>
/// <param name="x">x position of this texture to get the texture from</param>
/// <param name="y">y position of this texture to get the texture from</param>
/// <param name="width">width of the region to capture</param>
/// <param name="height">height of the region to capture</param>
public static Texture2D CreateTextureFromRegion(this Texture2D texture2D, int x, int y, int width, int height)
{
Color[] pixels = texture2D.GetPixels(x, y, width, height);
Texture2D destTex = new Texture2D(width, height);
destTex.SetPixels(pixels);
destTex.Apply();
return destTex;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Transform class
/// </summary>
public static class TransformExtensions
{
/// <summary>
/// An extension method that will get you the full path to an object.
/// </summary>
/// <param name="transform">The transform you wish a full path to.</param>
/// <param name="delimiter">The delimiter with which each object is delimited in the string.</param>
/// <param name="prefix">Prefix with which the full path to the object should start.</param>
/// <returns>A delimited string that is the full path to the game object in the hierarchy.</returns>
public static string GetFullPath(this Transform transform, string delimiter = ".", string prefix = "/")
{
var stringBuilder = new StringBuilder();
GetFullPath(stringBuilder, transform, delimiter, prefix);
return stringBuilder.ToString();
}
private static void GetFullPath(StringBuilder stringBuilder, Transform transform, string delimiter, string prefix)
{
if (transform.parent == null)
{
stringBuilder.Append(prefix);
}
else
{
GetFullPath(stringBuilder, transform.parent, delimiter, prefix);
stringBuilder.Append(delimiter);
}
stringBuilder.Append(transform.name);
}
/// <summary>
/// Enumerates all children in the hierarchy starting at the root object.
/// </summary>
/// <param name="root">Start point of the traversion set</param>
public static IEnumerable<Transform> EnumerateHierarchy(this Transform root)
{
if (root == null) { throw new ArgumentNullException("root"); }
return root.EnumerateHierarchyCore(new List<Transform>(0));
}
/// <summary>
/// Enumerates all children in the hierarchy starting at the root object except for the branches in ignore.
/// </summary>
/// <param name="root">Start point of the traversion set</param>
/// <param name="ignore">Transforms and all its children to be ignored</param>
public static IEnumerable<Transform> EnumerateHierarchy(this Transform root, ICollection<Transform> ignore)
{
if (root == null) { throw new ArgumentNullException("root"); }
if (ignore == null)
{
throw new ArgumentNullException("ignore", "Ignore collection can't be null, use EnumerateHierarchy(root) instead.");
}
return root.EnumerateHierarchyCore(ignore);
}
/// <summary>
/// Enumerates all children in the hierarchy starting at the root object except for the branches in ignore.
/// </summary>
/// <param name="root">Start point of the traversion set</param>
/// <param name="ignore">Transforms and all its children to be ignored</param>
private static IEnumerable<Transform> EnumerateHierarchyCore(this Transform root, ICollection<Transform> ignore)
{
var transformQueue = new Queue<Transform>();
transformQueue.Enqueue(root);
while (transformQueue.Count > 0)
{
var parentTransform = transformQueue.Dequeue();
if (!parentTransform || ignore.Contains(parentTransform)) { continue; }
for (var i = 0; i < parentTransform.childCount; i++)
{
transformQueue.Enqueue(parentTransform.GetChild(i));
}
yield return parentTransform;
}
}
/// <summary>
/// Calculates the bounds of all the colliders attached to this GameObject and all its children
/// </summary>
/// <param name="transform">Transform of root GameObject the colliders are attached to </param>
/// <returns>The total bounds of all colliders attached to this GameObject.
/// If no colliders attached, returns a bounds of center and extents 0</returns>
public static Bounds GetColliderBounds(this Transform transform)
{
Collider[] colliders = transform.GetComponentsInChildren<Collider>();
if (colliders.Length == 0) { return new Bounds(); }
Bounds bounds = colliders[0].bounds;
for (int i = 1; i < colliders.Length; i++)
{
bounds.Encapsulate(colliders[i].bounds);
}
return bounds;
}
/// <summary>
/// Checks if the provided transforms are child/parent related.
/// </summary>
/// <returns>True if either transform is the parent of the other or if they are the same</returns>
public static bool IsParentOrChildOf(this Transform transform1, Transform transform2)
{
return transform1.IsChildOf(transform2) || transform2.IsChildOf(transform1);
}
/// <summary>
/// Find the first component of type <typeparamref name="T"/> in the ancestors of the specified transform.
/// </summary>
/// <typeparam name="T">Type of component to find.</typeparam>
/// <param name="startTransform">Transform for which ancestors must be considered.</param>
/// <param name="includeSelf">Indicates whether the specified transform should be included.</param>
/// <returns>The component of type <typeparamref name="T"/>. Null if it none was found.</returns>
public static T FindAncestorComponent<T>(this Transform startTransform, bool includeSelf = true) where T : Component
{
foreach (Transform transform in startTransform.EnumerateAncestors(includeSelf))
{
T component = transform.GetComponent<T>();
if (component != null)
{
return component;
}
}
return null;
}
/// <summary>
/// Enumerates the ancestors of the specified transform.
/// </summary>
/// <param name="startTransform">Transform for which ancestors must be returned.</param>
/// <param name="includeSelf">Indicates whether the specified transform should be included.</param>
/// <returns>An enumeration of all ancestor transforms of the specified start transform.</returns>
public static IEnumerable<Transform> EnumerateAncestors(this Transform startTransform, bool includeSelf)
{
if (!includeSelf)
{
startTransform = startTransform.parent;
}
for (Transform transform = startTransform; transform != null; transform = transform.parent)
{
yield return transform;
}
}
/// <summary>
/// Transforms the size from local to world.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="localSize">The local size.</param>
/// <returns>World size.</returns>
public static Vector3 TransformSize(this Transform transform, Vector3 localSize)
{
Vector3 transformedSize = new Vector3(localSize.x, localSize.y, localSize.z);
Transform t = transform;
do
{
transformedSize.x *= t.localScale.x;
transformedSize.y *= t.localScale.y;
transformedSize.z *= t.localScale.z;
t = t.parent;
}
while (t != null);
return transformedSize;
}
/// <summary>
/// Transforms the size from world to local.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="worldSize">The world size</param>
/// <returns>World size.</returns>
public static Vector3 InverseTransformSize(this Transform transform, Vector3 worldSize)
{
Vector3 transformedSize = new Vector3(worldSize.x, worldSize.y, worldSize.z);
Transform t = transform;
do
{
transformedSize.x /= t.localScale.x;
transformedSize.y /= t.localScale.y;
transformedSize.z /= t.localScale.z;
t = t.parent;
}
while (t != null);
return transformedSize;
}
/// <summary>
/// Gets the hierarchical depth of the Transform from its root. Returns -1 if the transform is the root.
/// </summary>
/// <param name="t">The transform to get the depth for.</param>
public static int GetDepth(this Transform t)
{
int depth = -1;
Transform root = t.transform.root;
if (root == t.transform)
{
return depth;
}
TryGetDepth(t, root, ref depth);
return depth;
}
/// <summary>
/// Tries to get the hierarchical depth of the Transform from the specified parent. This method is recursive.
/// </summary>
/// <param name="target">The transform to get the depth for</param>
/// <param name="parent">The starting transform to look for the target transform in</param>
/// <param name="depth">The depth of the target transform</param>
/// <returns>'true' if the depth could be retrieved, or 'false' because the transform is a root transform.</returns>
public static bool TryGetDepth(Transform target, Transform parent, ref int depth)
{
foreach (Transform child in parent)
{
depth++;
if (child == target.transform || TryGetDepth(target, child, ref depth))
{
return true;
}
}
return false;
}
/// <summary>
/// Walk hierarchy looking for named transform
/// </summary>
/// <param name="t">root transform to start searching from</param>
/// <param name="name">name to look for</param>
/// <returns>returns found transform or null if none found</returns>
public static Transform GetChildRecursive(Transform t, string name)
{
int numChildren = t.childCount;
for (int ii = 0; ii < numChildren; ++ii)
{
Transform child = t.GetChild(ii);
if (child.name == name)
{
return child;
}
Transform foundIt = GetChildRecursive(child, name);
if (foundIt != null)
{
return foundIt;
}
}
return null;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
namespace Microsoft.MixedReality.Toolkit
{
public static class TypeExtensions
{
#if !NETFX_CORE
/// <summary>
/// Returns a list of types for all classes that extend from the current type and are not abstract
/// </summary>
/// <param name="rootType">The class type from which to search for inherited classes</param>
/// <param name="searchAssemblies">List of assemblies to search through for types. If null, default is to grab all assemblies in current app domain</param>
/// <returns>Null if rootType is not a class, otherwise returns list of types for sub-classes of rootType</returns>
public static List<Type> GetAllSubClassesOf(this Type rootType, Assembly[] searchAssemblies = null)
{
if (!rootType.IsClass) return null;
if (searchAssemblies == null) { searchAssemblies = AppDomain.CurrentDomain.GetAssemblies(); }
var results = new List<Type>();
Parallel.ForEach(searchAssemblies, (assembly) =>
{
Parallel.ForEach(assembly.GetTypes(), (type) =>
{
if (type != null && type.IsClass && !type.IsAbstract && type.IsSubclassOf(rootType))
{
results.Add(type);
}
});
});
return results;
}
#endif
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
using Object = UnityEngine.Object;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Object class
/// </summary>
public static class UnityObjectExtensions
{
/// <summary>
/// Enable Unity objects to skip "DontDestroyOnLoad" when editor isn't playing so test runner passes.
/// </summary>
public static void DontDestroyOnLoad(this Object target)
{
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlaying)
#endif
Object.DontDestroyOnLoad(target);
}
/// <summary>
/// Destroys a Unity object appropriately depending if running in edit or play mode.
/// </summary>
/// <param name="obj">Unity object to destroy</param>
/// <param name="t">Time in seconds at which to destroy the object, if applicable.</param>
public static void DestroyObject(Object obj, float t = 0.0f)
{
if (Application.isPlaying)
{
Object.Destroy(obj, t);
}
else
{
#if UNITY_EDITOR
// Must use DestroyImmediate in edit mode but it is not allowed when called from
// trigger/contact, animation event callbacks or OnValidate. Must use Destroy instead.
// Delay call to counter this issue in editor
UnityEditor.EditorApplication.delayCall += () =>
{
Object.DestroyImmediate(obj);
};
#else
Object.DestroyImmediate(obj);
#endif
}
}
/// <summary>
/// Tests if an interface is null, taking potential UnityEngine.Object derived class implementers into account
/// which require their overridden operators to be called
/// </summary>
/// <returns>True if either the managed or native object is null, false otherwise</returns>
public static bool IsNull<T>(this T @interface) where T : class => @interface == null || @interface.Equals(null);
/// <summary>
/// Properly checks an interface for null and returns the MonoBehaviour implementing it
/// </summary>
/// <returns> True if the implementer of the interface is not a MonoBehaviour or the MonoBehaviour is null</returns>
public static bool TryGetMonoBehaviour<T>(this T @interface, out MonoBehaviour monoBehaviour) where T : class => (monoBehaviour = @interface as MonoBehaviour) != null;
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Vector struct
/// </summary>
public static class VectorExtensions
{
public static Vector2 Mul(this Vector2 value, Vector2 scale)
{
return new Vector2(value.x * scale.x, value.y * scale.y);
}
public static Vector2 Div(this Vector2 value, Vector2 scale)
{
return new Vector2(value.x / scale.x, value.y / scale.y);
}
public static Vector3 Mul(this Vector3 value, Vector3 scale)
{
return new Vector3(value.x * scale.x, value.y * scale.y, value.z * scale.z);
}
public static Vector3 Div(this Vector3 value, Vector3 scale)
{
return new Vector3(value.x / scale.x, value.y / scale.y, value.z / scale.z);
}
public static Vector3 RotateAround(this Vector3 point, Vector3 pivot, Quaternion rotation)
{
return rotation * (point - pivot) + pivot;
}
public static Vector3 RotateAround(this Vector3 point, Vector3 pivot, Vector3 eulerAngles)
{
return RotateAround(point, pivot, Quaternion.Euler(eulerAngles));
}
public static Vector3 TransformPoint(this Vector3 point, Vector3 translation, Quaternion rotation, Vector3 lossyScale)
{
return rotation * Vector3.Scale(lossyScale, point) + translation;
}
public static Vector3 InverseTransformPoint(this Vector3 point, Vector3 translation, Quaternion rotation, Vector3 lossyScale)
{
var scaleInv = new Vector3(1 / lossyScale.x, 1 / lossyScale.y, 1 / lossyScale.z);
return Vector3.Scale(scaleInv, (Quaternion.Inverse(rotation) * (point - translation)));
}
public static Vector2 Average(this IEnumerable<Vector2> vectors)
{
float x = 0f;
float y = 0f;
int count = 0;
foreach (var pos in vectors)
{
x += pos.x;
y += pos.y;
count++;
}
return new Vector2(x / count, y / count);
}
public static Vector3 Average(this IEnumerable<Vector3> vectors)
{
float x = 0f;
float y = 0f;
float z = 0f;
int count = 0;
foreach (var pos in vectors)
{
x += pos.x;
y += pos.y;
z += pos.z;
count++;
}
return new Vector3(x / count, y / count, z / count);
}
public static Vector2 Average(this ICollection<Vector2> vectors)
{
int count = vectors.Count;
if (count == 0)
{
return Vector2.zero;
}
float x = 0f;
float y = 0f;
foreach (var pos in vectors)
{
x += pos.x;
y += pos.y;
}
return new Vector2(x / count, y / count);
}
public static Vector3 Average(this ICollection<Vector3> vectors)
{
int count = vectors.Count;
if (count == 0)
{
return Vector3.zero;
}
float x = 0f;
float y = 0f;
float z = 0f;
foreach (var pos in vectors)
{
x += pos.x;
y += pos.y;
z += pos.z;
}
return new Vector3(x / count, y / count, z / count);
}
public static Vector2 Median(this IEnumerable<Vector2> vectors)
{
var enumerable = vectors as Vector2[] ?? vectors.ToArray();
int count = enumerable.Length;
return count == 0 ? Vector2.zero : enumerable.OrderBy(v => v.sqrMagnitude).ElementAt(count / 2);
}
public static Vector3 Median(this IEnumerable<Vector3> vectors)
{
var enumerable = vectors as Vector3[] ?? vectors.ToArray();
int count = enumerable.Length;
return count == 0 ? Vector3.zero : enumerable.OrderBy(v => v.sqrMagnitude).ElementAt(count / 2);
}
public static Vector2 Median(this ICollection<Vector2> vectors)
{
int count = vectors.Count;
return count == 0 ? Vector2.zero : vectors.OrderBy(v => v.sqrMagnitude).ElementAt(count / 2);
}
public static Vector3 Median(this ICollection<Vector3> vectors)
{
int count = vectors.Count;
return count == 0 ? Vector3.zero : vectors.OrderBy(v => v.sqrMagnitude).ElementAt(count / 2);
}
public static bool IsValidVector(this Vector3 vector)
{
return !float.IsNaN(vector.x) && !float.IsNaN(vector.y) && !float.IsNaN(vector.z) &&
!float.IsInfinity(vector.x) && !float.IsInfinity(vector.y) && !float.IsInfinity(vector.z);
}
/// <summary>
/// Determines if the distance between two vectors is within a given tolerance.
/// </summary>
/// <param name="v1">The first vector.</param>
/// <param name="v2">The second vector.</param>
/// <param name="distanceTolerance">The maximum distance that will cause this to return true.</param>
/// <returns>True if the distance between the two vectors is within the tolerance, false otherwise.</returns>
public static bool CloseEnough(Vector3 v1, Vector3 v2, float distanceTolerance)
{
return Mathf.Abs(Vector3.Distance(v1, v2)) < distanceTolerance;
}
/// <summary>
/// Get the relative mapping based on a source Vec3 and a radius for spherical mapping.
/// </summary>
/// <param name="source">The source <see href="https://docs.unity3d.com/ScriptReference/Vector3.html">Vector3</see> to be mapped to sphere</param>
/// <param name="radius">This is a <see cref="float"/> for the radius of the sphere</param>
public static Vector3 SphericalMapping(Vector3 source, float radius)
{
float circ = 2f * Mathf.PI * radius;
float xAngle = (source.x / circ) * 360f;
float yAngle = -(source.y / circ) * 360f;
source.Set(0.0f, 0.0f, radius);
Quaternion rot = Quaternion.Euler(yAngle, xAngle, 0.0f);
source = rot * source;
return source;
}
/// <summary>
/// Get the relative mapping based on a source Vec3 and a radius for cylinder mapping.
/// </summary>
/// <param name="source">The source <see href="https://docs.unity3d.com/ScriptReference/Vector3.html">Vector3</see> to be mapped to cylinder</param>
/// <param name="radius">This is a <see cref="float"/> for the radius of the cylinder</param>
public static Vector3 CylindricalMapping(Vector3 source, float radius)
{
float circ = 2f * Mathf.PI * radius;
float xAngle = (source.x / circ) * 360f;
source.Set(0.0f, source.y, radius);
Quaternion rot = Quaternion.Euler(0.0f, xAngle, 0.0f);
source = rot * source;
return source;
}
/// <summary>
/// Get the relative mapping based on a source Vec3 and a radius for radial mapping.
/// </summary>
/// <param name="source">The source <see href="https://docs.unity3d.com/ScriptReference/Vector3.html">Vector3</see> to be mapped to cylinder</param>
/// <param name="radialRange">The total range of the radial in degrees as a <see cref="float"/></param>
/// <param name="radius">This is a <see cref="float"/> for the radius of the radial</param>
/// <param name="row">The current row as a <see cref="int"/> for the radial calculation</param>
/// <param name="totalRows">The total rows as a <see cref="int"/> for the radial calculation</param>
/// <param name="column">The current column as a <see cref="int"/> for the radial calculation</param>
/// <param name="totalColumns">The total columns as a <see cref="int"/> for the radial calculation</param>
public static Vector3 RadialMapping(Vector3 source, float radialRange, float radius, int row, int totalRows, int column, int totalColumns)
{
float radialCellAngle = radialRange / totalColumns;
source.x = 0f;
source.y = 0f;
source.z = (radius / totalRows) * row;
float yAngle = radialCellAngle * (column - (totalColumns * 0.5f)) + (radialCellAngle * .5f);
Quaternion rot = Quaternion.Euler(0.0f, yAngle, 0.0f);
source = rot * source;
return source;
}
/// <summary>
/// Randomized mapping based on a source Vec3 and a radius for randomization distance.
/// </summary>
/// <param name="source">The source <see href="https://docs.unity3d.com/ScriptReference/Vector3.html">Vector3</see> to be mapped to cylinder</param>
/// <param name="radius">This is a <see cref="float"/> for the radius of the cylinder</param>
public static Vector3 ScatterMapping(Vector3 source, float radius)
{
source.x = UnityEngine.Random.Range(-radius, radius);
source.y = UnityEngine.Random.Range(-radius, radius);
return source;
}
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
[assembly: System.Reflection.AssemblyVersion("2.5.0.0")]
[assembly: System.Reflection.AssemblyFileVersion("2.5.0.0")]
[assembly: System.Reflection.AssemblyProduct("Microsoft® Mixed Reality Toolkit aipmragent_work")]
[assembly: System.Reflection.AssemblyCopyright("Copyright © Microsoft Corporation")]
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
/// <summary>
/// A custom editor for the ClippingBox to allow for specification of the framing bounds.
/// </summary>
[CustomEditor(typeof(ClippingBox))]
[CanEditMultipleObjects]
public class ClippingBoxEditor : ClippingPrimitiveEditor
{
/// <inheritdoc/>
protected override bool HasFrameBounds()
{
return true;
}
/// <inheritdoc/>
protected override Bounds OnGetFrameBounds()
{
var primitive = target as ClippingBox;
Debug.Assert(primitive != null);
return new Bounds(primitive.transform.position, primitive.transform.lossyScale);
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
/// <summary>
/// A custom editor for the ClippingPlaneEditor to allow for specification of the framing bounds.
/// </summary>
[CustomEditor(typeof(ClippingPlane))]
[CanEditMultipleObjects]
public class ClippingPlaneEditor : ClippingPrimitiveEditor
{
/// <inheritdoc/>
protected override bool HasFrameBounds()
{
return true;
}
/// <inheritdoc/>
protected override Bounds OnGetFrameBounds()
{
var primitive = target as ClippingPlane;
Debug.Assert(primitive != null);
return new Bounds(primitive.transform.position, Vector3.one);
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
/// <summary>
/// An abstract editor component to improve the editor experience with ClippingPrimitives.
/// </summary>
[CustomEditor(typeof(ClippingPrimitive))]
[CanEditMultipleObjects]
public abstract class ClippingPrimitiveEditor : UnityEditor.Editor
{
/// <summary>
/// Notifies the Unity editor if this object has custom frame bounds.
/// </summary>
/// <returns>True if custom frame bounds can be used from OnGetFrameBounds.</returns>
protected abstract bool HasFrameBounds();
/// <summary>
/// Returns the bounds the editor should focus on.
/// </summary>
/// <returns>The bounds of the clipping primitive.</returns>
protected abstract Bounds OnGetFrameBounds();
private ClippingPrimitive clippingPrimitive;
private void OnEnable()
{
clippingPrimitive = (ClippingPrimitive)target;
}
/// <summary>
/// Looks for changes to the list of renderers and gracefully adds and removes them.
/// </summary>
public override void OnInspectorGUI()
{
var previousRenderers = clippingPrimitive.GetRenderersCopy();
using (var check = new EditorGUI.ChangeCheckScope())
{
DrawDefaultInspector();
if (check.changed)
{
// Flagging changes other than renderers
clippingPrimitive.IsDirty = true;
}
}
var currentRenderers = clippingPrimitive.GetRenderersCopy();
// Add or remove and renderers that were added or removed via the inspector.
foreach (var renderer in previousRenderers.Except(currentRenderers))
{
clippingPrimitive.RemoveRenderer(renderer);
}
foreach (var renderer in currentRenderers.Except(previousRenderers))
{
clippingPrimitive.AddRenderer(renderer);
}
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
/// <summary>
/// A custom editor for the ClippingSphere to allow for specification of the framing bounds.
/// </summary>
[CustomEditor(typeof(ClippingSphere))]
[CanEditMultipleObjects]
public class ClippingSphereEditor : ClippingPrimitiveEditor
{
/// <inheritdoc/>
protected override bool HasFrameBounds()
{
return true;
}
/// <inheritdoc/>
protected override Bounds OnGetFrameBounds()
{
var primitive = target as ClippingSphere;
Debug.Assert(primitive != null);
return new Bounds(primitive.transform.position, Vector3.one * primitive.Radius);
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using Microsoft.MixedReality.Toolkit.Input.Editor;
using Microsoft.MixedReality.Toolkit.Utilities.Editor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
public class ControllerPopupWindow : EditorWindow
{
private const string EditorWindowOptionsPath = "Inspectors/Data/EditorWindowOptions.json";
private const float InputActionLabelWidth = 128f;
/// <summary>
/// Used to enable editing the input axis label positions on controllers
/// </summary>
private static readonly bool EnableWysiwyg = false;
private static readonly GUIContent InteractionAddButtonContent = new GUIContent("+ Add a New Interaction Mapping");
private static readonly GUIContent InteractionMinusButtonContent = new GUIContent("-", "Remove Interaction Mapping");
private static readonly GUIContent AxisTypeContent = new GUIContent("Axis Type", "The axis type of the button, e.g. Analogue, Digital, etc.");
private static readonly GUIContent ControllerInputTypeContent = new GUIContent("Input Type", "The primary action of the input as defined by the controller SDK.");
private static readonly GUIContent ActionContent = new GUIContent("Action", "Action to be raised to the Input Manager when the input data has changed.");
private static readonly GUIContent KeyCodeContent = new GUIContent("KeyCode", "Unity Input KeyCode id to listen for.");
private static readonly GUIContent XAxisContent = new GUIContent("X Axis", "Horizontal Axis to listen for.");
private static readonly GUIContent YAxisContent = new GUIContent("Y Axis", "Vertical Axis to listen for.");
private static readonly GUIContent InvertContent = new GUIContent("Invert", "Should an Axis be inverted?");
private static readonly GUIContent[] InvertAxisContent =
{
new GUIContent("None"),
new GUIContent("X"),
new GUIContent("Y"),
new GUIContent("Both")
};
private static readonly int[] InvertAxisValues = { 0, 1, 2, 3 };
private static readonly Vector2 InputActionLabelPosition = new Vector2(256f, 0f);
private static readonly Vector2 InputActionDropdownPosition = new Vector2(88f, 0f);
private static readonly Vector2 InputActionFlipTogglePosition = new Vector2(-24f, 0f);
private static readonly Vector2 HorizontalSpace = new Vector2(8f, 0f);
private static readonly Rect ControllerRectPosition = new Rect(new Vector2(128f, 0f), new Vector2(512f, 512f));
private static ControllerPopupWindow window;
private static ControllerInputActionOptions controllerInputActionOptions;
private static GUIContent[] axisLabels;
private static int[] actionIds;
private static GUIContent[] actionLabels;
private static int[] rawActionIds;
private static GUIContent[] rawActionLabels;
private static int[] digitalActionIds;
private static GUIContent[] digitalActionLabels;
private static int[] singleAxisActionIds;
private static GUIContent[] singleAxisActionLabels;
private static int[] dualAxisActionIds;
private static GUIContent[] dualAxisActionLabels;
private static int[] threeDofPositionActionIds;
private static GUIContent[] threeDofPositionActionLabels;
private static int[] threeDofRotationActionIds;
private static GUIContent[] threeDofRotationActionLabels;
private static int[] sixDofActionIds;
private static GUIContent[] sixDofActionLabels;
private static bool[] isMouseInRects;
private static bool editInputActionPositions;
private static float defaultLabelWidth;
private static float defaultFieldWidth;
private static Vector2 horizontalScrollPosition;
private SerializedProperty currentInteractionList;
private List<string> mappedControllerList = new List<string>();
private ControllerPopupWindow thisWindow;
private MixedRealityControllerMapping currentControllerMapping;
private Vector2 mouseDragOffset;
private GUIStyle flippedLabelStyle;
private Texture2D currentControllerTexture;
private ControllerInputActionOption currentControllerOption;
private void OnFocus()
{
currentControllerTexture = ControllerMappingLibrary.GetControllerTexture(currentControllerMapping.ControllerType, currentControllerMapping.Handedness);
#region Interaction Constraint Setup
actionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
axisLabels = ControllerMappingLibrary.UnityInputManagerAxes
.Select(axis => new GUIContent(axis.Name))
.Prepend(new GUIContent("None")).ToArray();
actionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.None)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
actionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.None)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
rawActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.Raw)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
rawActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.Raw)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
digitalActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.Digital)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
digitalActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.Digital)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
singleAxisActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.SingleAxis)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
singleAxisActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.SingleAxis)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
dualAxisActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.DualAxis)
.Select(action => (int)action.Id).Prepend(0).ToArray();
dualAxisActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.DualAxis)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
threeDofPositionActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.ThreeDofPosition)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
threeDofPositionActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.ThreeDofPosition)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
threeDofRotationActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.ThreeDofRotation)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
threeDofRotationActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.ThreeDofRotation)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
sixDofActionIds = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.SixDof)
.Select(action => (int)action.Id)
.Prepend(0).ToArray();
sixDofActionLabels = MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions
.Where(inputAction => inputAction.AxisConstraint == AxisType.SixDof)
.Select(inputAction => new GUIContent(inputAction.Description))
.Prepend(new GUIContent("None")).ToArray();
#endregion Interaction Constraint Setup
}
public static void Show(MixedRealityControllerMapping controllerMapping, SerializedProperty interactionsList, Handedness handedness = Handedness.None, List<string> mappedControllers = null)
{
if (window != null)
{
window.Close();
}
window = null;
window = CreateInstance<ControllerPopupWindow>();
window.thisWindow = window;
window.titleContent = new GUIContent($"{controllerMapping.Description} - Input Action Assignment");
window.mappedControllerList = mappedControllers;
window.currentControllerMapping = controllerMapping;
window.currentInteractionList = interactionsList;
isMouseInRects = new bool[interactionsList.arraySize];
string editorWindowOptionsPath = ResolveEditorWindowOptionsPath();
if (!File.Exists(editorWindowOptionsPath))
{
var empty = new ControllerInputActionOptions
{
Controllers = new List<ControllerInputActionOption>
{
new ControllerInputActionOption
{
Controller = 0,
Handedness = Handedness.None,
InputLabelPositions = new[] {new Vector2(0, 0)},
IsLabelFlipped = new []{false}
}
}
};
File.WriteAllText(editorWindowOptionsPath, JsonUtility.ToJson(empty, true));
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
else
{
controllerInputActionOptions = JsonUtility.FromJson<ControllerInputActionOptions>(File.ReadAllText(editorWindowOptionsPath));
if (controllerInputActionOptions.Controllers.Any(option => option.Controller == controllerMapping.SupportedControllerType && option.Handedness == handedness))
{
window.currentControllerOption = controllerInputActionOptions.Controllers.FirstOrDefault(option => option.Controller == controllerMapping.SupportedControllerType && option.Handedness == handedness);
if (window.currentControllerOption != null && window.currentControllerOption.IsLabelFlipped == null)
{
window.currentControllerOption.IsLabelFlipped = new bool[interactionsList.arraySize];
}
}
}
var windowSize = new Vector2(controllerMapping.HasCustomInteractionMappings ? 896f : 768f, 512f);
window.maxSize = windowSize;
window.minSize = windowSize;
window.CenterOnMainWin();
window.ShowUtility();
defaultLabelWidth = EditorGUIUtility.labelWidth;
defaultFieldWidth = EditorGUIUtility.fieldWidth;
}
private void Update()
{
if (editInputActionPositions)
{
Repaint();
}
}
private void OnGUI()
{
if (flippedLabelStyle == null)
{
flippedLabelStyle = new GUIStyle("Label")
{
alignment = TextAnchor.UpperRight,
stretchWidth = true
};
}
if (!currentControllerMapping.HasCustomInteractionMappings && currentControllerTexture != null)
{
GUILayout.BeginHorizontal();
GUI.DrawTexture(ControllerRectPosition, currentControllerTexture);
GUILayout.EndHorizontal();
}
try
{
RenderInteractionList(currentInteractionList, currentControllerMapping.HasCustomInteractionMappings);
RenderMappingList(mappedControllerList);
}
catch (Exception)
{
thisWindow.Close();
}
}
private void RenderMappingList(List<string> controllerList)
{
if (controllerList == null)
{
return;
}
GUIStyle headerStyle = new GUIStyle();
headerStyle.richText = true;
if (currentControllerOption == null || currentControllerTexture == null)
{
GUILayout.BeginVertical();
using (new EditorGUILayout.VerticalScope())
{
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField("<b>Controllers affected by this mapping</b>", headerStyle);
for (int i = 0; i < controllerList.Count; i++)
{
EditorGUILayout.LabelField(controllerList[i]);
}
}
GUILayout.EndVertical();
}
else
{
float max_y = currentControllerOption.InputLabelPositions.Max(x => x.y);
var titleRectPosition = Vector2.up * (max_y + 4 * EditorGUIUtility.singleLineHeight);
var titleRectSize = new Vector2(500, EditorGUIUtility.singleLineHeight);
var titleRect = new Rect(titleRectPosition, titleRectSize);
EditorGUI.LabelField(titleRect, "<b>Controllers affected by this mapping</b>", headerStyle);
for (int i = 0; i < controllerList.Count; i++)
{
var rectPosition = Vector2.up * (max_y + (i+5) * EditorGUIUtility.singleLineHeight);
var rectSize = new Vector2(1000, EditorGUIUtility.singleLineHeight);
var labelRect = new Rect(rectPosition, rectSize);
EditorGUI.LabelField(labelRect, controllerList[i]);
}
}
}
private void RenderInteractionList(SerializedProperty interactionList, bool useCustomInteractionMapping)
{
if (interactionList == null) { throw new Exception(); }
bool noInteractions = interactionList.arraySize == 0;
if (currentControllerOption != null && (currentControllerOption.IsLabelFlipped == null || currentControllerOption.IsLabelFlipped.Length != interactionList.arraySize))
{
currentControllerOption.IsLabelFlipped = new bool[interactionList.arraySize];
}
GUILayout.BeginVertical();
if (useCustomInteractionMapping)
{
horizontalScrollPosition = EditorGUILayout.BeginScrollView(horizontalScrollPosition, false, false, GUILayout.ExpandWidth(true), GUILayout.ExpandWidth(true));
}
if (useCustomInteractionMapping)
{
if (GUILayout.Button(InteractionAddButtonContent))
{
interactionList.arraySize += 1;
var interaction = interactionList.GetArrayElementAtIndex(interactionList.arraySize - 1);
var axisType = interaction.FindPropertyRelative("axisType");
axisType.enumValueIndex = 0;
var inputType = interaction.FindPropertyRelative("inputType");
inputType.enumValueIndex = 0;
var action = interaction.FindPropertyRelative("inputAction");
var actionId = action.FindPropertyRelative("id");
var actionDescription = action.FindPropertyRelative("description");
actionDescription.stringValue = "None";
actionId.intValue = 0;
}
if (noInteractions)
{
EditorGUILayout.HelpBox("Create an Interaction Mapping.", MessageType.Warning);
EditorGUILayout.EndScrollView();
GUILayout.EndVertical();
return;
}
}
else if (EnableWysiwyg)
{
EditorGUI.BeginChangeCheck();
editInputActionPositions = EditorGUILayout.Toggle("Edit Input Action Positions", editInputActionPositions);
if (EditorGUI.EndChangeCheck())
{
string editorWindowOptionsPath = ResolveEditorWindowOptionsPath();
if (!editInputActionPositions)
{
File.WriteAllText(editorWindowOptionsPath, JsonUtility.ToJson(controllerInputActionOptions, true));
}
else
{
if (!controllerInputActionOptions.Controllers.Any(
option => option.Controller == currentControllerMapping.SupportedControllerType && option.Handedness == currentControllerMapping.Handedness))
{
currentControllerOption = new ControllerInputActionOption
{
Controller = currentControllerMapping.SupportedControllerType,
Handedness = currentControllerMapping.Handedness,
InputLabelPositions = new Vector2[currentInteractionList.arraySize],
IsLabelFlipped = new bool[currentInteractionList.arraySize]
};
controllerInputActionOptions.Controllers.Add(currentControllerOption);
isMouseInRects = new bool[currentInteractionList.arraySize];
if (controllerInputActionOptions.Controllers.Any(option => option.Controller == 0))
{
controllerInputActionOptions.Controllers.Remove(
controllerInputActionOptions.Controllers.Find(option =>
option.Controller == 0));
}
File.WriteAllText(editorWindowOptionsPath, JsonUtility.ToJson(controllerInputActionOptions, true));
}
}
}
}
GUILayout.BeginHorizontal();
if (useCustomInteractionMapping)
{
EditorGUILayout.LabelField("Id", GUILayout.Width(32f));
EditorGUIUtility.labelWidth = 24f;
EditorGUIUtility.fieldWidth = 24f;
EditorGUILayout.LabelField(ControllerInputTypeContent, GUILayout.Width(InputActionLabelWidth));
EditorGUILayout.LabelField(AxisTypeContent, GUILayout.Width(InputActionLabelWidth));
EditorGUILayout.LabelField(ActionContent, GUILayout.Width(InputActionLabelWidth));
EditorGUILayout.LabelField(KeyCodeContent, GUILayout.Width(InputActionLabelWidth));
EditorGUILayout.LabelField(XAxisContent, GUILayout.Width(InputActionLabelWidth));
EditorGUILayout.LabelField(YAxisContent, GUILayout.Width(InputActionLabelWidth));
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField(string.Empty, GUILayout.Width(24f));
EditorGUIUtility.labelWidth = defaultLabelWidth;
EditorGUIUtility.fieldWidth = defaultFieldWidth;
}
GUILayout.EndHorizontal();
for (int i = 0; i < interactionList.arraySize; i++)
{
using (new EditorGUILayout.HorizontalScope())
{
SerializedProperty interaction = interactionList.GetArrayElementAtIndex(i);
if (useCustomInteractionMapping)
{
EditorGUILayout.LabelField($"{i + 1}", GUILayout.Width(32f));
var inputType = interaction.FindPropertyRelative("inputType");
EditorGUILayout.PropertyField(inputType, GUIContent.none, GUILayout.Width(InputActionLabelWidth));
var axisType = interaction.FindPropertyRelative("axisType");
EditorGUILayout.PropertyField(axisType, GUIContent.none, GUILayout.Width(InputActionLabelWidth));
var invertXAxis = interaction.FindPropertyRelative("invertXAxis");
var invertYAxis = interaction.FindPropertyRelative("invertYAxis");
var interactionAxisConstraint = interaction.FindPropertyRelative("axisType");
var action = interaction.FindPropertyRelative("inputAction");
var actionId = action.FindPropertyRelative("id");
var actionDescription = action.FindPropertyRelative("description");
var actionConstraint = action.FindPropertyRelative("axisConstraint");
GUIContent[] labels;
int[] ids;
switch ((AxisType)interactionAxisConstraint.intValue)
{
default:
case AxisType.None:
labels = actionLabels;
ids = actionIds;
break;
case AxisType.Raw:
labels = rawActionLabels;
ids = rawActionIds;
break;
case AxisType.Digital:
labels = digitalActionLabels;
ids = digitalActionIds;
break;
case AxisType.SingleAxis:
labels = singleAxisActionLabels;
ids = singleAxisActionIds;
break;
case AxisType.DualAxis:
labels = dualAxisActionLabels;
ids = dualAxisActionIds;
break;
case AxisType.ThreeDofPosition:
labels = threeDofPositionActionLabels;
ids = threeDofPositionActionIds;
break;
case AxisType.ThreeDofRotation:
labels = threeDofRotationActionLabels;
ids = threeDofRotationActionIds;
break;
case AxisType.SixDof:
labels = sixDofActionLabels;
ids = sixDofActionIds;
break;
}
EditorGUI.BeginChangeCheck();
actionId.intValue = EditorGUILayout.IntPopup(GUIContent.none, actionId.intValue, labels, ids, GUILayout.Width(InputActionLabelWidth));
if (EditorGUI.EndChangeCheck())
{
var inputAction = actionId.intValue == 0 ? MixedRealityInputAction.None : MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions[actionId.intValue - 1];
actionDescription.stringValue = inputAction.Description;
actionConstraint.enumValueIndex = (int)inputAction.AxisConstraint;
}
if ((AxisType)axisType.intValue == AxisType.Digital)
{
var keyCode = interaction.FindPropertyRelative("keyCode");
EditorGUILayout.PropertyField(keyCode, GUIContent.none, GUILayout.Width(InputActionLabelWidth));
}
else
{
if ((AxisType)axisType.intValue == AxisType.DualAxis)
{
EditorGUIUtility.labelWidth = InputActionLabelWidth * 0.5f;
EditorGUIUtility.fieldWidth = InputActionLabelWidth * 0.5f;
int currentAxisSetting = 0;
if (invertXAxis.boolValue)
{
currentAxisSetting += 1;
}
if (invertYAxis.boolValue)
{
currentAxisSetting += 2;
}
EditorGUI.BeginChangeCheck();
currentAxisSetting = EditorGUILayout.IntPopup(InvertContent, currentAxisSetting, InvertAxisContent, InvertAxisValues, GUILayout.Width(InputActionLabelWidth));
if (EditorGUI.EndChangeCheck())
{
switch (currentAxisSetting)
{
case 0:
invertXAxis.boolValue = false;
invertYAxis.boolValue = false;
break;
case 1:
invertXAxis.boolValue = true;
invertYAxis.boolValue = false;
break;
case 2:
invertXAxis.boolValue = false;
invertYAxis.boolValue = true;
break;
case 3:
invertXAxis.boolValue = true;
invertYAxis.boolValue = true;
break;
}
}
EditorGUIUtility.labelWidth = defaultLabelWidth;
EditorGUIUtility.fieldWidth = defaultFieldWidth;
}
else if ((AxisType)axisType.intValue == AxisType.SingleAxis)
{
invertXAxis.boolValue = EditorGUILayout.ToggleLeft("Invert X", invertXAxis.boolValue, GUILayout.Width(InputActionLabelWidth));
EditorGUIUtility.labelWidth = defaultLabelWidth;
}
else
{
EditorGUILayout.LabelField(GUIContent.none, GUILayout.Width(InputActionLabelWidth));
}
}
if ((AxisType)axisType.intValue == AxisType.SingleAxis ||
(AxisType)axisType.intValue == AxisType.DualAxis)
{
var axisCodeX = interaction.FindPropertyRelative("axisCodeX");
RenderAxisPopup(axisCodeX, InputActionLabelWidth);
}
else
{
EditorGUILayout.LabelField(GUIContent.none, GUILayout.Width(InputActionLabelWidth));
}
if ((AxisType)axisType.intValue == AxisType.DualAxis)
{
var axisCodeY = interaction.FindPropertyRelative("axisCodeY");
RenderAxisPopup(axisCodeY, InputActionLabelWidth);
}
else
{
EditorGUILayout.LabelField(GUIContent.none, GUILayout.Width(InputActionLabelWidth));
}
if (GUILayout.Button(InteractionMinusButtonContent, EditorStyles.miniButtonRight, GUILayout.ExpandWidth(true)))
{
interactionList.DeleteArrayElementAtIndex(i);
}
}
else
{
var interactionDescription = interaction.FindPropertyRelative("description");
var interactionAxisConstraint = interaction.FindPropertyRelative("axisType");
var action = interaction.FindPropertyRelative("inputAction");
var actionId = action.FindPropertyRelative("id");
var actionDescription = action.FindPropertyRelative("description");
var actionConstraint = action.FindPropertyRelative("axisConstraint");
GUIContent[] labels;
int[] ids;
switch ((AxisType)interactionAxisConstraint.intValue)
{
default:
case AxisType.None:
labels = actionLabels;
ids = actionIds;
break;
case AxisType.Raw:
labels = rawActionLabels;
ids = rawActionIds;
break;
case AxisType.Digital:
labels = digitalActionLabels;
ids = digitalActionIds;
break;
case AxisType.SingleAxis:
labels = singleAxisActionLabels;
ids = singleAxisActionIds;
break;
case AxisType.DualAxis:
labels = dualAxisActionLabels;
ids = dualAxisActionIds;
break;
case AxisType.ThreeDofPosition:
labels = threeDofPositionActionLabels;
ids = threeDofPositionActionIds;
break;
case AxisType.ThreeDofRotation:
labels = threeDofRotationActionLabels;
ids = threeDofRotationActionIds;
break;
case AxisType.SixDof:
labels = sixDofActionLabels;
ids = sixDofActionIds;
break;
}
EditorGUI.BeginChangeCheck();
if (currentControllerOption == null || currentControllerTexture == null)
{
bool skip = false;
var description = interactionDescription.stringValue;
if (currentControllerMapping.SupportedControllerType == SupportedControllerType.GGVHand
&& currentControllerMapping.Handedness == Handedness.None)
{
if (description != "Select")
{
skip = true;
}
}
if (!skip)
{
actionId.intValue = EditorGUILayout.IntPopup(GUIContent.none, actionId.intValue, labels, ids, GUILayout.Width(80f));
EditorGUILayout.LabelField(description, GUILayout.ExpandWidth(true));
}
}
else
{
var rectPosition = currentControllerOption.InputLabelPositions[i];
var rectSize = InputActionLabelPosition + InputActionDropdownPosition + new Vector2(currentControllerOption.IsLabelFlipped[i] ? 0f : 8f, EditorGUIUtility.singleLineHeight);
GUI.Box(new Rect(rectPosition, rectSize), GUIContent.none, EditorGUIUtility.isProSkin ? "ObjectPickerBackground" : "ObjectPickerResultsEven");
var offset = currentControllerOption.IsLabelFlipped[i] ? InputActionLabelPosition : Vector2.zero;
var popupRect = new Rect(rectPosition + offset, new Vector2(InputActionDropdownPosition.x, EditorGUIUtility.singleLineHeight));
actionId.intValue = EditorGUI.IntPopup(popupRect, actionId.intValue, labels, ids);
offset = currentControllerOption.IsLabelFlipped[i] ? Vector2.zero : InputActionDropdownPosition;
var labelRect = new Rect(rectPosition + offset, new Vector2(InputActionLabelPosition.x, EditorGUIUtility.singleLineHeight));
EditorGUI.LabelField(labelRect, interactionDescription.stringValue, currentControllerOption.IsLabelFlipped[i] ? flippedLabelStyle : EditorStyles.label);
if (editInputActionPositions)
{
offset = currentControllerOption.IsLabelFlipped[i] ? InputActionLabelPosition + InputActionDropdownPosition + HorizontalSpace : InputActionFlipTogglePosition;
var toggleRect = new Rect(rectPosition + offset, new Vector2(-InputActionFlipTogglePosition.x, EditorGUIUtility.singleLineHeight));
EditorGUI.BeginChangeCheck();
currentControllerOption.IsLabelFlipped[i] = EditorGUI.Toggle(toggleRect, currentControllerOption.IsLabelFlipped[i]);
if (EditorGUI.EndChangeCheck())
{
if (currentControllerOption.IsLabelFlipped[i])
{
currentControllerOption.InputLabelPositions[i] -= InputActionLabelPosition;
}
else
{
currentControllerOption.InputLabelPositions[i] += InputActionLabelPosition;
}
}
if (!isMouseInRects.Any(value => value) || isMouseInRects[i])
{
if (Event.current.type == EventType.MouseDrag && labelRect.Contains(Event.current.mousePosition) && !isMouseInRects[i])
{
isMouseInRects[i] = true;
mouseDragOffset = Event.current.mousePosition - currentControllerOption.InputLabelPositions[i];
}
else if (Event.current.type == EventType.Repaint && isMouseInRects[i])
{
currentControllerOption.InputLabelPositions[i] = Event.current.mousePosition - mouseDragOffset;
}
else if (Event.current.type == EventType.DragUpdated && isMouseInRects[i])
{
currentControllerOption.InputLabelPositions[i] = Event.current.mousePosition - mouseDragOffset;
}
else if (Event.current.type == EventType.MouseUp && isMouseInRects[i])
{
currentControllerOption.InputLabelPositions[i] = Event.current.mousePosition - mouseDragOffset;
mouseDragOffset = Vector2.zero;
isMouseInRects[i] = false;
}
}
}
}
if (EditorGUI.EndChangeCheck())
{
MixedRealityInputAction inputAction = actionId.intValue == 0 ?
MixedRealityInputAction.None :
MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions[actionId.intValue - 1];
actionId.intValue = (int)inputAction.Id;
actionDescription.stringValue = inputAction.Description;
actionConstraint.enumValueIndex = (int)inputAction.AxisConstraint;
interactionList.serializedObject.ApplyModifiedProperties();
}
}
}
}
if (useCustomInteractionMapping)
{
EditorGUILayout.EndScrollView();
interactionList.serializedObject.ApplyModifiedProperties();
}
GUILayout.EndVertical();
}
private static void RenderAxisPopup(SerializedProperty axisCode, float customLabelWidth)
{
var axisId = -1;
for (int j = 0; j < ControllerMappingLibrary.UnityInputManagerAxes.Length; j++)
{
if (ControllerMappingLibrary.UnityInputManagerAxes[j].Name == axisCode.stringValue)
{
axisId = j + 1;
break;
}
}
EditorGUI.BeginChangeCheck();
axisId = EditorGUILayout.IntPopup(GUIContent.none, axisId, axisLabels, null, GUILayout.Width(customLabelWidth));
if (EditorGUI.EndChangeCheck())
{
if (axisId == 0)
{
axisCode.stringValue = string.Empty;
axisCode.serializedObject.ApplyModifiedProperties();
}
else
{
for (int j = 0; j < ControllerMappingLibrary.UnityInputManagerAxes.Length; j++)
{
if (axisId - 1 == j)
{
axisCode.stringValue = ControllerMappingLibrary.UnityInputManagerAxes[j].Name;
axisCode.serializedObject.ApplyModifiedProperties();
break;
}
}
}
}
}
private static string ResolveEditorWindowOptionsPath()
{
return MixedRealityToolkitFiles.MapRelativeFilePathToAbsolutePath(EditorWindowOptionsPath);
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input.Editor
{
/// <summary>
/// Used to aid in layout of Controller Input Actions.
/// </summary>
[Serializable]
public class ControllerInputActionOption
{
public SupportedControllerType Controller;
public Handedness Handedness;
public Vector2[] InputLabelPositions;
public bool[] IsLabelFlipped;
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment