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,650 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrController3DModel.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Avatar;
using UltimateXR.Avatar.Rig;
using UltimateXR.Core;
using UltimateXR.Core.Components;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
/// <summary>
/// Represents the 3D model of a VR controller. It allows to graphically render the current position/orientation and
/// input state of the device.
/// </summary>
public class UxrController3DModel : UxrComponent
{
#region Inspector Properties/Serialized Fields
[SerializeField] private bool _needsBothHands;
[SerializeField] private UxrHandSide _handSide;
[SerializeField] private UxrControllerHand _controllerHand;
[SerializeField] private UxrControllerHand _controllerHandLeft;
[SerializeField] private UxrControllerHand _controllerHandRight;
[SerializeField] private Transform _forward;
[SerializeField] private List<UxrElement> _controllerElements = new List<UxrElement>();
#endregion
#region Public Types & Data
/// <summary>
/// Gets whether the controller requires two hands to hold it.
/// </summary>
public bool NeedsBothHands => _needsBothHands;
/// <summary>
/// Gets the hand required to hold the controller, if <see cref="NeedsBothHands" /> is false.
/// </summary>
public UxrHandSide HandSide => _handSide;
/// <summary>
/// Gets the forward transform as it is currently in the scene. It can be different than the actual forward tracking
/// when we use grab mechanics because the hand transform can be modified by the grab manager and the controller
/// usually hangs from the hand hierarchy.
/// If you need to know the forward controller transform using the information of tracking sensors without any
/// intervention by external elements like the grabbing mechanics use <see cref="ForwardTrackingRotation" />.
/// </summary>
public Transform Forward => _forward;
/// <summary>
/// Gets the rotation that represents the controller's forward orientation. We use this mainly to be able to align
/// certain mechanics no matter the controller that is currently active. A gun in a game needs to be aligned to the
/// controller, teleport mechanics, etc.
/// </summary>
public Quaternion ForwardTrackingRotation
{
get
{
IUxrControllerTracking controllerTracking = _avatar != null ? _avatar.FirstControllerTracking : null;
if (controllerTracking == null)
{
return _forward.rotation;
}
Quaternion relativeRotation = Quaternion.Inverse(transform.rotation) * _forward.transform.rotation;
Quaternion sensorRotation = _handSide == UxrHandSide.Left ? controllerTracking.SensorLeftRot : controllerTracking.SensorRightRot;
return sensorRotation * relativeRotation;
}
}
/// <summary>
/// Gets or sets the hand that is interacting with the controller, when the controller is used with only one hand.
/// </summary>
public UxrControllerHand ControllerHand
{
get => _controllerHand;
set => _controllerHand = value;
}
/// <summary>
/// Gets or sets the left hand that is interacting with the controller, when the controller can be held using both
/// hands.
/// </summary>
public UxrControllerHand ControllerHandLeft
{
get => _controllerHandLeft;
set => _controllerHandLeft = value;
}
/// <summary>
/// Gets or sets the right hand that is interacting with the controller, when the controller can be held using both
/// hands.
/// </summary>
public UxrControllerHand ControllerHandRight
{
get => _controllerHandRight;
set => _controllerHandRight = value;
}
/// <summary>
/// Gets or sets whether the controller is visible.
/// </summary>
public bool IsControllerVisible
{
get => _isControllerVisible;
set
{
_isControllerVisible = value;
gameObject.SetActive(_isControllerVisible);
}
}
/// <summary>
/// Gets or sets whether the hand, if present, is visible. In setups where both hands are used, it targets visibility
/// of both hands.
/// </summary>
public bool IsHandVisible
{
get => _isHandVisible;
set
{
_isHandVisible = value;
if (_controllerHand != null)
{
_controllerHand.gameObject.SetActive(_isHandVisible);
}
if (_controllerHandLeft != null)
{
_controllerHandLeft.gameObject.SetActive(_isHandVisible);
}
if (_controllerHandRight != null)
{
_controllerHandRight.gameObject.SetActive(_isHandVisible);
}
}
}
#endregion
#region Public Methods
/// <summary>
/// Updates the current visual state using the given input.
/// </summary>
/// <param name="controllerInput">The input device to update the controller with</param>
/// <param name="onlyIfControllerHand">Whether to update the visual state only if a controller hand is currently assigned</param>
public void UpdateFromInput(UxrControllerInput controllerInput, bool onlyIfControllerHand = false)
{
if (controllerInput == null)
{
return;
}
foreach (UxrFingerType fingerType in Enum.GetValues(typeof(UxrFingerType)))
{
if (fingerType != UxrFingerType.None)
{
_fingerContacts[fingerType].Transform = null;
_fingerContactsLeft[fingerType].Transform = null;
_fingerContactsRight[fingerType].Transform = null;
}
}
foreach (UxrElement element in _controllerElements)
{
if (element.ElementObject == null)
{
continue;
}
// Update controller element
bool contact = false;
UxrInputButtons controllerButton = UxrControllerInput.ControllerElementToButton(element.Element);
switch (element.ElementType)
{
case UxrElementType.Button:
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
element.ElementObject.transform.localPosition = element.InitialLocalPos;
}
else
{
if (controllerInput.GetButtonsPress(element.HandSide, controllerButton, true))
{
element.ElementObject.transform.localPosition = element.InitialLocalPos +
element.LocalOffsetX * element.ButtonPressedOffset.x +
element.LocalOffsetY * element.ButtonPressedOffset.y +
element.LocalOffsetZ * element.ButtonPressedOffset.z;
}
else
{
element.ElementObject.transform.localPosition = element.InitialLocalPos;
}
}
break;
case UxrElementType.Input1DRotate:
float inputRotateValue = 0.0f;
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
}
else
{
inputRotateValue = controllerInput.GetInput1D(element.HandSide, UxrControllerInput.ControllerElementToInput1D(element.Element), true);
}
Vector3 euler = element.Input1DPressedOffsetAngle * inputRotateValue;
element.ElementObject.transform.localRotation = element.InitialLocalRot * Quaternion.Euler(euler);
contact = contact || inputRotateValue > 0.01f;
break;
case UxrElementType.Input1DPush:
float inputPushValue = 0.0f;
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
}
else
{
inputPushValue = controllerInput.GetInput1D(element.HandSide, UxrControllerInput.ControllerElementToInput1D(element.Element), true);
}
Vector3 offset = element.Input1DPressedOffset * inputPushValue;
element.ElementObject.transform.localPosition = element.InitialLocalPos + element.LocalOffsetX * offset.x + element.LocalOffsetY * offset.y + element.LocalOffsetZ * offset.z;
contact = contact || inputPushValue > 0.01f;
break;
case UxrElementType.Input2DJoystick:
Vector2 inputValueJoystick = Vector2.zero;
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
}
else
{
inputValueJoystick = controllerInput.GetInput2D(element.HandSide, UxrControllerInput.ControllerElementToInput2D(element.Element), true);
}
Vector3 euler1 = Vector3.Lerp(-element.Input2DFirstAxisOffsetAngle, element.Input2DFirstAxisOffsetAngle, (inputValueJoystick.x + 1.0f) * 0.5f);
Vector3 euler2 = Vector3.Lerp(-element.Input2DSecondAxisOffsetAngle, element.Input2DSecondAxisOffsetAngle, (inputValueJoystick.y + 1.0f) * 0.5f);
element.ElementObject.transform.localRotation = Quaternion.Euler(euler2) * Quaternion.Euler(euler1) * element.InitialLocalRot;
contact = contact || inputValueJoystick != Vector2.zero;
break;
case UxrElementType.Input2DTouch:
Vector2 inputValueTouch = controllerInput.GetInput2D(element.HandSide, UxrControllerInput.ControllerElementToInput2D(element.Element), true);
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
}
else
{
inputValueTouch = controllerInput.GetInput2D(element.HandSide, UxrControllerInput.ControllerElementToInput2D(element.Element), true);
}
Vector3 offset1 = Vector3.Lerp(-element.Input2DFirstAxisOffset, element.Input2DFirstAxisOffset, (inputValueTouch.x + 1.0f) * 0.5f);
Vector3 offset2 = Vector3.Lerp(-element.Input2DSecondAxisOffset, element.Input2DSecondAxisOffset, (inputValueTouch.y + 1.0f) * 0.5f);
element.FingerContactPoint.transform.localPosition = element.FingerContactInitialLocalPos +
element.LocalFingerPosOffsetX * offset1.x + element.LocalFingerPosOffsetY * offset1.y + element.LocalFingerPosOffsetZ * offset1.z +
element.LocalFingerPosOffsetX * offset2.x + element.LocalFingerPosOffsetY * offset2.y + element.LocalFingerPosOffsetZ * offset2.z;
contact = contact || inputValueTouch != Vector2.zero;
break;
case UxrElementType.DPad:
bool dpadLeft = false;
bool dpadRight = false;
bool dpadUp = false;
bool dpadDown = false;
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
}
else
{
dpadLeft = controllerInput.GetButtonsPress(element.HandSide, UxrInputButtons.DPadLeft, true);
dpadRight = controllerInput.GetButtonsPress(element.HandSide, UxrInputButtons.DPadRight, true);
dpadUp = controllerInput.GetButtonsPress(element.HandSide, UxrInputButtons.DPadUp, true);
dpadDown = controllerInput.GetButtonsPress(element.HandSide, UxrInputButtons.DPadDown, true);
}
Vector3 dpadEuler1 = dpadLeft ? -element.DpadFirstAxisOffsetAngle :
dpadRight ? element.DpadFirstAxisOffsetAngle : Vector3.zero;
Vector3 dpadEuler2 = dpadUp ? -element.DpadSecondAxisOffsetAngle :
dpadDown ? element.DpadSecondAxisOffsetAngle : Vector3.zero;
Vector3 dpadOffset1 = dpadLeft ? -element.DpadFirstAxisOffset :
dpadRight ? element.DpadFirstAxisOffset : Vector3.zero;
Vector3 dpadOffset2 = dpadUp ? -element.DpadSecondAxisOffset :
dpadDown ? element.DpadSecondAxisOffset : Vector3.zero;
element.ElementObject.transform.localRotation = Quaternion.Euler(dpadEuler2) * Quaternion.Euler(dpadEuler1) * element.InitialLocalRot;
element.FingerContactPoint.transform.localPosition = element.FingerContactInitialLocalPos +
element.LocalFingerPosOffsetX * dpadOffset1.x +
element.LocalFingerPosOffsetY * dpadOffset1.y +
element.LocalFingerPosOffsetZ * dpadOffset1.z +
element.LocalFingerPosOffsetX * dpadOffset2.x +
element.LocalFingerPosOffsetY * dpadOffset2.y +
element.LocalFingerPosOffsetZ * dpadOffset2.z;
contact = contact || dpadLeft || dpadRight || dpadUp || dpadDown;
break;
case UxrElementType.NotSet: break;
}
// Update finger contact?
contact = contact || (controllerButton != UxrInputButtons.None && (controllerInput.GetButtonsTouch(element.HandSide, controllerButton, true) || controllerInput.GetButtonsPress(element.HandSide, controllerButton, true)));
if (onlyIfControllerHand && !IsControllerHandPresent(element.HandSide))
{
contact = false;
}
if (element.FingerContactPoint == null)
{
continue;
}
if (element.FingerContactPoint != element.ElementObject)
{
bool handVisible = _controllerHand && _controllerHand.gameObject.activeSelf;
if (_needsBothHands)
{
handVisible = (element.HandSide == UxrHandSide.Left && _controllerHandLeft != null && _controllerHandLeft.gameObject.activeSelf) ||
(element.HandSide == UxrHandSide.Right && _controllerHandRight != null && _controllerHandRight.gameObject.activeSelf);
}
element.FingerContactPoint.SetActive(contact && !handVisible);
}
if (!contact || element.Finger == UxrFingerType.None)
{
continue;
}
if (_needsBothHands == false)
{
_fingerContacts[element.Finger].Transform = element.FingerContactPoint.transform;
}
else
{
switch (element.HandSide)
{
case UxrHandSide.Left:
_fingerContactsLeft[element.Finger].Transform = element.FingerContactPoint.transform;
break;
case UxrHandSide.Right:
_fingerContactsRight[element.Finger].Transform = element.FingerContactPoint.transform;
break;
default: throw new ArgumentOutOfRangeException();
}
}
}
// Update fingers
if (_needsBothHands == false)
{
if (_controllerHand != null && _fingerContacts != null)
{
foreach (KeyValuePair<UxrFingerType, UxrFingerContactInfo> fingerTransformPair in _fingerContacts)
{
_controllerHand.UpdateFinger(fingerTransformPair.Key, fingerTransformPair.Value);
}
}
}
else
{
if (_controllerHandLeft != null && _fingerContactsLeft != null)
{
foreach (KeyValuePair<UxrFingerType, UxrFingerContactInfo> fingerTransformPair in _fingerContactsLeft)
{
_controllerHandLeft.UpdateFinger(fingerTransformPair.Key, fingerTransformPair.Value);
}
}
if (_controllerHandRight != null && _fingerContactsRight != null)
{
foreach (KeyValuePair<UxrFingerType, UxrFingerContactInfo> fingerTransformPair in _fingerContactsRight)
{
_controllerHandRight.UpdateFinger(fingerTransformPair.Key, fingerTransformPair.Value);
}
}
}
}
/// <summary>
/// Gets the list of GameObjects that represent the given different controller input elements.
/// </summary>
/// <param name="elements">Flags representing the input elements to get the objects of</param>
/// <returns>List of GameObjects representing the given controller input elements</returns>
public IEnumerable<GameObject> GetElements(UxrControllerElements elements)
{
foreach (UxrControllerElements element in ControllerElements)
{
if (elements.HasFlag(element) && _hashedElements.TryGetValue(element, out GameObject elementGameObject))
{
yield return elementGameObject;
}
}
}
/// <summary>
/// Gets the list of materials of all objects that represent the given different controller input elements.
/// </summary>
/// <param name="elements">Flags representing the input elements to get the materials from</param>
/// <returns>List of materials used by the objects representing the given controller input elements</returns>
public IEnumerable<Material> GetElementsMaterials(UxrControllerElements elements)
{
foreach (UxrControllerElements element in ControllerElements)
{
if (elements.HasFlag(element) && _hashedElements.TryGetValue(element, out GameObject elementGameObject))
{
Renderer elementRenderer = elementGameObject.GetComponent<Renderer>();
if (elementRenderer != null && elementRenderer.material != null)
{
yield return elementRenderer.material;
}
}
}
}
/// <summary>
/// Gets the list of original shared materials of all objects that represent the given different controller input
/// elements. The original materials are the shared materials that the input elements had at the beginning, before any
/// modifications.
/// </summary>
/// <param name="elements">Flags representing the input elements to get the original shared materials from</param>
/// <returns>List of original shared materials used by the objects representing the given controller input elements</returns>
public IEnumerable<Material> GetElementsOriginalMaterials(UxrControllerElements elements)
{
foreach (UxrControllerElements element in ControllerElements)
{
if (elements.HasFlag(element) && _hashedElementsOriginalMaterial.TryGetValue(element, out Material elementMaterial))
{
yield return elementMaterial;
}
}
}
/// <summary>
/// Changes the material of the objects that represent the given different controller input elements.
/// </summary>
/// <param name="elements">Flags representing the input elements whose materials will be changed</param>
/// <param name="material">New material to assign</param>
public void SetElementsMaterial(UxrControllerElements elements, Material material)
{
foreach (UxrControllerElements element in ControllerElements)
{
if (elements.HasFlag(element) && _hashedElements.TryGetValue(element, out GameObject elementGameObject)
&& elementGameObject.TryGetComponent<Renderer>(out var elementRenderer))
{
elementRenderer.material = material;
}
}
}
/// <summary>
/// Restores the materials of the objects that represent the given different controller input elements.
/// </summary>
/// <param name="elements">Flags representing the input elements whose materials to restore</param>
public void RestoreElementsMaterials(UxrControllerElements elements)
{
foreach (UxrControllerElements element in ControllerElements)
{
if (elements.HasFlag(element) && _hashedElements.TryGetValue(element, out GameObject elementGameObject)
&& elementGameObject.TryGetComponent<Renderer>(out var elementRenderer))
{
elementRenderer.sharedMaterial = _hashedElementsOriginalMaterial[element];
}
}
}
/// <summary>
/// Changes the current hand to use the controller to the opposite side.
/// </summary>
public void SwitchHandedness()
{
if (_needsBothHands)
{
return;
}
_handSide = _handSide == UxrHandSide.Left ? UxrHandSide.Right : UxrHandSide.Left;
foreach (UxrElement element in _controllerElements)
{
element.HandSide = element.HandSide == UxrHandSide.Left ? UxrHandSide.Right : UxrHandSide.Left;
}
}
#endregion
#region Unity
/// <summary>
/// Initializes the component.
/// </summary>
protected override void Awake()
{
base.Awake();
// Initialize data
_avatar = GetComponentInParent<UxrAvatar>();
foreach (UxrFingerType fingerType in Enum.GetValues(typeof(UxrFingerType)))
{
if (fingerType != UxrFingerType.None)
{
_fingerContacts.Add(fingerType, new UxrFingerContactInfo(null));
_fingerContactsLeft.Add(fingerType, new UxrFingerContactInfo(null));
_fingerContactsRight.Add(fingerType, new UxrFingerContactInfo(null));
}
}
if (_controllerElements != null)
{
foreach (UxrElement element in _controllerElements)
{
if (element.ElementObject != null)
{
// Initialize initial pos/rot
element.InitialLocalPos = element.ElementObject.transform.localPosition;
element.InitialLocalRot = element.ElementObject.transform.localRotation;
// Initialize original materials and hashed elements
if (_hashedElements.ContainsKey(element.Element))
{
//Debug.LogWarning($"Element {element.Element} was already found in the {nameof(UxrController3DModel)} list of {name}. Ignoring.");
}
else
{
// Element
_hashedElements.Add(element.Element, element.ElementObject);
// Original materials
Renderer renderer = element.ElementObject.GetComponent<Renderer>();
_hashedElementsOriginalMaterial.Add(element.Element, renderer != null ? renderer.sharedMaterial : null);
}
element.LocalOffsetX = element.ElementObject.transform.parent.InverseTransformDirection(element.ElementObject.transform.right);
element.LocalOffsetY = element.ElementObject.transform.parent.InverseTransformDirection(element.ElementObject.transform.up);
element.LocalOffsetZ = element.ElementObject.transform.parent.InverseTransformDirection(element.ElementObject.transform.forward);
if (element.FingerContactPoint != null)
{
element.LocalFingerPosOffsetX = element.FingerContactPoint.transform.parent.InverseTransformDirection(element.ElementObject.transform.right);
element.LocalFingerPosOffsetY = element.FingerContactPoint.transform.parent.InverseTransformDirection(element.ElementObject.transform.up);
element.LocalFingerPosOffsetZ = element.FingerContactPoint.transform.parent.InverseTransformDirection(element.ElementObject.transform.forward);
}
}
if (element.ElementObject != null && element.FingerContactPoint != null)
{
element.FingerContactInitialLocalPos = element.FingerContactPoint.transform.localPosition;
if (element.FingerContactPoint != element.ElementObject)
{
element.FingerContactPoint.SetActive(false);
}
}
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Gets whether the component has a visual hand available for visualization.
/// </summary>
/// <param name="handSide">Hand to check for</param>
/// <returns>Whether there is a visual hand available</returns>
private bool IsControllerHandPresent(UxrHandSide handSide)
{
if (_needsBothHands)
{
return handSide == UxrHandSide.Left ? _controllerHandLeft != null : _controllerHandRight != null;
}
return _controllerHand != null;
}
#endregion
#region Private Types & Data
/// <summary>
/// Gets all the possible elements in a controller.
/// </summary>
private IEnumerable<UxrControllerElements> ControllerElements
{
get
{
foreach (var value in Enum.GetValues(typeof(UxrControllerElements)))
{
UxrControllerElements element = (UxrControllerElements)value;
if (element != UxrControllerElements.None && element != UxrControllerElements.Everything)
{
yield return element;
}
}
}
}
private readonly Dictionary<UxrControllerElements, GameObject> _hashedElements = new Dictionary<UxrControllerElements, GameObject>();
private readonly Dictionary<UxrControllerElements, Material> _hashedElementsOriginalMaterial = new Dictionary<UxrControllerElements, Material>();
private readonly Dictionary<UxrFingerType, UxrFingerContactInfo> _fingerContacts = new Dictionary<UxrFingerType, UxrFingerContactInfo>();
private readonly Dictionary<UxrFingerType, UxrFingerContactInfo> _fingerContactsLeft = new Dictionary<UxrFingerType, UxrFingerContactInfo>();
private readonly Dictionary<UxrFingerType, UxrFingerContactInfo> _fingerContactsRight = new Dictionary<UxrFingerType, UxrFingerContactInfo>();
private UxrAvatar _avatar;
private bool _isControllerVisible = true;
private bool _isHandVisible = true;
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a2285bc0022316448ac5c40fd488b38a
timeCreated: 1504766639
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrControllerHand.FingerInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UltimateXR.Animation.IK;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
public partial class UxrControllerHand
{
#region Private Types & Data
/// <summary>
/// Describes a finger used by a hand when interacting with a VR controller. It allows to graphically represent fingers
/// that interact with VR controllers by using Inverse Kinematics.
/// </summary>
[Serializable]
private class FingerIK
{
#region Inspector Properties/Serialized Fields
[SerializeField] private UxrCcdIKSolver _fingerIKSolver;
[SerializeField] private float _fingerToGoalDuration = 0.1f;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the finger IK solver.
/// </summary>
public UxrCcdIKSolver FingerIKSolver => _fingerIKSolver;
/// <summary>
/// Gets the seconds it will take for a finger to reach the input element whenever it is pressed.
/// </summary>
public float FingerToGoalDuration => _fingerToGoalDuration;
/// <summary>
/// Gets or sets whether the component data has been initialized at runtime using
/// <see cref="UxrControllerHand.InitializeFinger" />.
/// </summary>
public bool Initialized { get; set; }
/// <summary>
/// Gets or sets the effector initial local position. The effector is the part of the finger in the IK chain that will
/// try to reach the goal.
/// </summary>
public Vector3 LocalEffectorInitialPos { get; set; }
/// <summary>
/// Gets or sets the initial local position of the IK goal in a transition to a pressed state.
/// </summary>
public Vector3 LocalGoalTransitionStartPos { get; set; }
/// <summary>
/// Gets or sets the current goal transform. The goal is the transform that the finger will try to reach whenever the
/// input element is pressed.
/// </summary>
public Transform CurrentFingerGoal { get; set; }
/// <summary>
/// The or sets current timer in the transition to a pressed state, to smoothly transition the finger from its default
/// position to the pressed position.
/// </summary>
public float TimerToGoal { get; set; }
/// <summary>
/// Gets or sets whether the component is enabled. If it is disabled no IK will take place.
/// </summary>
public bool ComponentEnabled { get; set; }
#endregion
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1ed85e5d55d14f5dbadfd5dcb07a7e36
timeCreated: 1643731645

View File

@@ -0,0 +1,52 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrControllerHand.ObjectVariation.MaterialVariation.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
public partial class UxrControllerHand
{
#region Public Types & Data
public partial class ObjectVariation
{
#region Public Types & Data
/// <summary>
/// Defines a Material variation in the different materials available for a hand GameObject.
/// </summary>
[Serializable]
public class MaterialVariation
{
#region Inspector Properties/Serialized Fields
[SerializeField] private string _name;
[SerializeField] private Material _material;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the variation name.
/// </summary>
public string Name => _name;
/// <summary>
/// Gets the variation material.
/// </summary>
public Material Material => _material;
#endregion
}
#endregion
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f6ab31a68ba4d94bcbf82c79787c139
timeCreated: 1651753651

View File

@@ -0,0 +1,52 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrControllerHand.ObjectVariation.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
public partial class UxrControllerHand
{
#region Public Types & Data
/// <summary>
/// Defines a GameObject variation in the different hands that are available in the component.
/// </summary>
[Serializable]
public partial class ObjectVariation
{
#region Inspector Properties/Serialized Fields
[SerializeField] private string _name;
[SerializeField] private GameObject _gameObject;
[SerializeField] private List<MaterialVariation> _materialVariations;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the variation name.
/// </summary>
public string Name => _name;
/// <summary>
/// Gets the variation object.
/// </summary>
public GameObject GameObject => _gameObject;
/// <summary>
/// Gets the material variations.
/// </summary>
public IEnumerable<MaterialVariation> MaterialVariations => _materialVariations;
#endregion
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9acb5164948d4a228d66670135a3c308
timeCreated: 1651753643

View File

@@ -0,0 +1,289 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrControllerHand.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using UltimateXR.Avatar;
using UltimateXR.Avatar.Rig;
using UltimateXR.Core;
using UltimateXR.Core.Components.Composite;
using UltimateXR.Manipulation.HandPoses;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
/// <summary>
/// Component that represents a hand holding a VR controller. It allows to graphically render a hand that
/// mimics the interaction that the user performs on a VR controller.
/// </summary>
[DisallowMultipleComponent]
public partial class UxrControllerHand : UxrAvatarComponent<UxrControllerHand>
{
#region Inspector Properties/Serialized Fields
[SerializeField] private bool _hasAvatarSource;
[SerializeField] private UxrAvatar _avatarPrefab;
[SerializeField] private UxrHandSide _avatarHandSide;
[SerializeField] private UxrHandPoseAsset _handPose;
[SerializeField] private List<ObjectVariation> _variations;
[SerializeField] private UxrAvatarHand _hand;
[SerializeField] private FingerIK _thumb;
[SerializeField] private FingerIK _index;
[SerializeField] private FingerIK _middle;
[SerializeField] private FingerIK _ring;
[SerializeField] private FingerIK _little;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the hand references.
/// </summary>
public UxrAvatarHand Hand => _hand;
/// <summary>
/// Gets the hand variations, if there are any. Variations allow to provide different visual representations of the
/// hand. It can be different objects and each object may have different materials.
/// </summary>
public IEnumerable<ObjectVariation> Variations => _variations;
#endregion
#region Public Methods
/// <summary>
/// Initializes the component when the controller hand is dynamic, such as when used through an
/// <see cref="UxrAvatar" /> that changes poses.
/// </summary>
/// <param name="avatar">Avatar the hand belongs to. The current enabled pose will be used to initialize the finger IK.</param>
/// <param name="handSide">Which hand side</param>
public void InitializeFromCurrentHandPose(UxrAvatar avatar, UxrHandSide handSide)
{
UxrRuntimeHandPose runtimeHandPose = avatar.GetCurrentRuntimeHandPose(handSide);
if (runtimeHandPose != null)
{
// Take snapshot of transforms
var transforms = UxrAvatarRig.PushHandTransforms(avatar.GetHand(handSide));
// Briefly switch to pose. This is used because we may be in the middle of a transition and the pose hasn't been acquired yet.
UxrAvatarRig.UpdateHandUsingRuntimeDescriptor(avatar, handSide, runtimeHandPose.GetHandDescriptor(handSide));
// Initialize IK
InitializeFinger(_thumb, true);
InitializeFinger(_index, true);
InitializeFinger(_middle, true);
InitializeFinger(_ring, true);
InitializeFinger(_little, true);
// Restore transforms from snapshot
UxrAvatarRig.PopHandTransforms(avatar.GetHand(handSide), transforms);
}
}
/// <summary>
/// Updates a given finger.
/// </summary>
/// <param name="finger">Finger to update</param>
/// <param name="fingerContactInfo">Finger contact information</param>
public void UpdateFinger(UxrFingerType finger, UxrFingerContactInfo fingerContactInfo)
{
if (_fingers == null)
{
return;
}
if (_fingers.TryGetValue(finger, out FingerIK fingerInfo) && fingerInfo.Initialized && fingerInfo.FingerIKSolver)
{
Transform fingerIKParent = fingerInfo.FingerIKSolver.Links[0].Bone.parent;
if (fingerInfo.CurrentFingerGoal != fingerContactInfo.Transform)
{
fingerInfo.CurrentFingerGoal = fingerContactInfo.Transform;
fingerInfo.TimerToGoal = fingerInfo.FingerToGoalDuration;
fingerInfo.LocalGoalTransitionStartPos = fingerIKParent.InverseTransformPoint(fingerInfo.FingerIKSolver.Goal.position);
}
if (fingerInfo.TimerToGoal > 0.0f)
{
fingerInfo.TimerToGoal -= Time.deltaTime;
float t = 1.0f - Mathf.Clamp01(fingerInfo.TimerToGoal / fingerInfo.FingerToGoalDuration);
fingerInfo.FingerIKSolver.Goal.position = Vector3.Lerp(fingerIKParent.TransformPoint(fingerInfo.LocalGoalTransitionStartPos),
fingerContactInfo.Transform != null ? fingerContactInfo.Transform.position : fingerIKParent.TransformPoint(fingerInfo.LocalEffectorInitialPos),
t);
if (fingerContactInfo.Transform != null)
{
fingerInfo.FingerIKSolver.SolverEnabled = true;
}
}
else
{
if (fingerContactInfo.Transform != null)
{
fingerInfo.FingerIKSolver.Goal.position = fingerContactInfo.Transform.position;
}
else
{
fingerInfo.FingerIKSolver.SolverEnabled = false;
}
}
}
}
/// <summary>
/// Allows to manually update the Inverse Kinematics of all fingers.
/// </summary>
public void UpdateIKManually()
{
foreach (KeyValuePair<UxrFingerType, FingerIK> fingerPair in _fingers)
{
if (fingerPair.Value.FingerIKSolver && fingerPair.Value.FingerIKSolver.SolverEnabled)
{
fingerPair.Value.FingerIKSolver.SolveIK();
}
}
}
#endregion
#region Unity
/// <summary>
/// Generates the internal list of fingers.
/// </summary>
protected override void Awake()
{
base.Awake();
_fingers = new Dictionary<UxrFingerType, FingerIK>();
_thumb.ComponentEnabled = _thumb.FingerIKSolver != null && _thumb.FingerIKSolver.enabled;
_index.ComponentEnabled = _index.FingerIKSolver != null && _index.FingerIKSolver.enabled;
_middle.ComponentEnabled = _middle.FingerIKSolver != null && _middle.FingerIKSolver.enabled;
_ring.ComponentEnabled = _ring.FingerIKSolver != null && _ring.FingerIKSolver.enabled;
_little.ComponentEnabled = _little.FingerIKSolver != null && _little.FingerIKSolver.enabled;
_fingers.Add(UxrFingerType.Thumb, _thumb);
_fingers.Add(UxrFingerType.Index, _index);
_fingers.Add(UxrFingerType.Middle, _middle);
_fingers.Add(UxrFingerType.Ring, _ring);
_fingers.Add(UxrFingerType.Little, _little);
}
/// <summary>
/// Enables the finger IK solvers.
/// </summary>
protected override void OnEnable()
{
base.OnEnable();
UxrManager.StageUpdating += UxrManager_StageUpdating;
}
/// <summary>
/// Disables the finger IK solvers.
/// </summary>
protected override void OnDisable()
{
base.OnDisable();
UxrManager.StageUpdating -= UxrManager_StageUpdating;
}
/// <summary>
/// Make sure the hand rig is allocated when the component is reset.
/// </summary>
protected override void Reset()
{
base.Reset();
_hand = new UxrAvatarHand();
}
/// <summary>
/// Initializes the fingers.
/// </summary>
protected override void Start()
{
base.Start();
if (Avatar == null)
{
UxrManager.LogMissingAvatarInHierarchyError(this);
}
InitializeFinger(_thumb);
InitializeFinger(_index);
InitializeFinger(_middle);
InitializeFinger(_ring);
InitializeFinger(_little);
}
#endregion
#region Event Handling Methods
/// <summary>
/// Called when the <see cref="UxrManager" /> is about to update a stage.
/// </summary>
/// <param name="stage"></param>
private void UxrManager_StageUpdating(UxrUpdateStage stage)
{
if (stage == UxrUpdateStage.Animation)
{
// Check resetting bone transforms?
}
}
#endregion
#region Private Methods
/// <summary>
/// Initializes a finger.
/// </summary>
/// <param name="finger">The finger to initialize</param>
/// <param name="recomputeLinkData">
/// Whether to recompute the link data. This is required if the initial pose for the IK
/// changed
/// </param>
private void InitializeFinger(FingerIK finger, bool recomputeLinkData = false)
{
if (finger.FingerIKSolver != null)
{
if (recomputeLinkData)
{
finger.FingerIKSolver.ComputeLinkData();
}
else
{
// Initializing the finger for the first time from the component itself.
// This makes sure that the data is computed with the initial finger bone orientations.
finger.FingerIKSolver.RestoreInitialRotations();
}
finger.Initialized = true;
finger.FingerIKSolver.enabled = finger.ComponentEnabled;
finger.LocalEffectorInitialPos = finger.FingerIKSolver.Links[0].Bone.parent.InverseTransformPoint(finger.FingerIKSolver.EndEffector.position);
finger.FingerIKSolver.Goal.SetPositionAndRotation(finger.FingerIKSolver.EndEffector.position, finger.FingerIKSolver.EndEffector.rotation);
finger.CurrentFingerGoal = null;
finger.TimerToGoal = -1.0f;
}
}
#endregion
#region Private Types & Data
private Dictionary<UxrFingerType, FingerIK> _fingers;
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2e3aec586ed72f74a8f71a9fc93449d4
timeCreated: 1505111961
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrElement.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UltimateXR.Avatar.Rig;
using UltimateXR.Core;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
/// <summary>
/// Describes the properties of a VR controller input element.
/// </summary>
[Serializable]
public class UxrElement
{
#region Inspector Properties/Serialized Fields
[SerializeField] private UxrElementType _elementType = UxrElementType.NotSet;
[SerializeField] private UxrHandSide _hand;
[SerializeField] private UxrControllerElements _element;
[SerializeField] private GameObject _gameObject;
[SerializeField] private UxrFingerType _finger;
[SerializeField] private GameObject _fingerContactPoint;
[SerializeField] private Vector3 _buttonPressedOffset;
[SerializeField] private Vector3 _input1DPressedOffsetAngle;
[SerializeField] private Vector3 _input1DPressedOffset;
[SerializeField] private Vector3 _input2DFirstAxisOffsetAngle;
[SerializeField] private Vector3 _input2DSecondAxisOffsetAngle;
[SerializeField] private Vector3 _input2DFirstAxisOffset;
[SerializeField] private Vector3 _input2DSecondAxisOffset;
[SerializeField] private Vector3 _dpadFirstAxisOffsetAngle;
[SerializeField] private Vector3 _dpadSecondAxisOffsetAngle;
[SerializeField] private Vector3 _dpadFirstAxisOffset;
[SerializeField] private Vector3 _dpadSecondAxisOffset;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the input element type.
/// </summary>
public UxrElementType ElementType => _elementType;
/// <summary>
/// Gets which controller element(s) the input element describes.
/// </summary>
public UxrControllerElements Element => _element;
/// <summary>
/// Gets the object that represents the input element.
/// </summary>
public GameObject ElementObject => _gameObject;
/// <summary>
/// Gets which finger interacts with the input element.
/// </summary>
public UxrFingerType Finger => _finger;
/// <summary>
/// Gets the finger contact point if there is any. If null it will try to contact <see cref="ElementObject" />'s
/// transform.
/// </summary>
public GameObject FingerContactPoint => _fingerContactPoint;
/// <summary>
/// Gets the pressed offset for a <see cref="UxrElementType.Button" /> input element.
/// </summary>
public Vector3 ButtonPressedOffset => _buttonPressedOffset;
/// <summary>
/// Gets the pressed offset euler angles for a <see cref="UxrElementType.Input1DRotate" /> input element.
/// </summary>
public Vector3 Input1DPressedOffsetAngle => _input1DPressedOffsetAngle;
/// <summary>
/// Gets the pressed offset for a <see cref="UxrElementType.Input1DPush" /> input element.
/// </summary>
public Vector3 Input1DPressedOffset => _input1DPressedOffset;
/// <summary>
/// Gets the maximum positive angle range for the first axis in a <see cref="UxrElementType.Input2DJoystick" />
/// input element. The other side will be the negated angle.
/// </summary>
public Vector3 Input2DFirstAxisOffsetAngle => _input2DFirstAxisOffsetAngle;
/// <summary>
/// Gets the maximum positive angle range for the second axis in a <see cref="UxrElementType.Input2DJoystick" />
/// input element. The other side will be the negated angle.
/// </summary>
public Vector3 Input2DSecondAxisOffsetAngle => _input2DSecondAxisOffsetAngle;
/// <summary>
/// Gets the maximum positive offset of the first axis in a <see cref="UxrElementType.Input2DTouch" /> input
/// element. The other side will be the negated offset.
/// </summary>
public Vector3 Input2DFirstAxisOffset => _input2DFirstAxisOffset;
/// <summary>
/// Gets the maximum positive offset of the second axis in a <see cref="UxrElementType.Input2DTouch" /> input
/// element. The other side will be the negated offset.
/// </summary>
public Vector3 Input2DSecondAxisOffset => _input2DSecondAxisOffset;
/// <summary>
/// Gets the maximum positive angle range for the first axis in a <see cref="UxrElementType.DPad" /> input element. The
/// other side will be the negated angle.
/// </summary>
public Vector3 DpadFirstAxisOffsetAngle => _dpadFirstAxisOffsetAngle;
/// <summary>
/// Gets the maximum positive angle range for the second axis in a <see cref="UxrElementType.DPad" /> input element.
/// The other side will be the negated angle.
/// </summary>
public Vector3 DpadSecondAxisOffsetAngle => _dpadSecondAxisOffsetAngle;
/// <summary>
/// Gets the maximum positive offset for the first axis in a <see cref="UxrElementType.DPad" /> input element. The
/// other side will be the negated offset.
/// </summary>
public Vector3 DpadFirstAxisOffset => _dpadFirstAxisOffset;
/// <summary>
/// Gets the maximum positive offset for the second axis in a <see cref="UxrElementType.DPad" /> input element. The
/// other side will be the negated offset.
/// </summary>
public Vector3 DpadSecondAxisOffset => _dpadSecondAxisOffset;
/// <summary>
/// Gets the hand that is used to interact with the input.
/// </summary>
public UxrHandSide HandSide
{
get => _hand;
internal set => _hand = value;
}
#endregion
#region Internal Types & Data
/// <summary>
/// Gets or sets the transform's initial local position.
/// </summary>
internal Vector3 InitialLocalPos { get; set; }
/// <summary>
/// Gets or sets the transform's initial local rotation.
/// </summary>
internal Quaternion InitialLocalRot { get; set; }
/// <summary>
/// Gets or sets the initial local position of the finger point of contact.
/// </summary>
internal Vector3 FingerContactInitialLocalPos { get; set; }
/// <summary>
/// Gets or sets the local right (X) offset axis.
/// </summary>
internal Vector3 LocalOffsetX { get; set; }
/// <summary>
/// Gets or sets the local up (Y) offset axis.
/// </summary>
internal Vector3 LocalOffsetY { get; set; }
/// <summary>
/// Gets or sets the local forward (Z) offset axis.
/// </summary>
internal Vector3 LocalOffsetZ { get; set; }
/// <summary>
/// Gets or sets the local right (X) offset of the finger point of contact.
/// </summary>
internal Vector3 LocalFingerPosOffsetX { get; set; }
/// <summary>
/// Gets or sets the local up (Y) offset of the finger point of contact.
/// </summary>
internal Vector3 LocalFingerPosOffsetY { get; set; }
/// <summary>
/// Gets or sets the local forward (Z) offset of the finger point of contact.
/// </summary>
internal Vector3 LocalFingerPosOffsetZ { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 960381cdea0c4db386cf72679d198700
timeCreated: 1643101938

View File

@@ -0,0 +1,48 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrElementType.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace UltimateXR.Devices.Visualization
{
/// <summary>
/// Enumerates the different input element types in a VR controller.
/// </summary>
public enum UxrElementType
{
/// <summary>
/// Not set.
/// </summary>
NotSet,
/// <summary>
/// A button.
/// </summary>
Button,
/// <summary>
/// An analog button that is rotated.
/// </summary>
Input1DRotate,
/// <summary>
/// An analog button that is pushed.
/// </summary>
Input1DPush,
/// <summary>
/// An analog joystick.
/// </summary>
Input2DJoystick,
/// <summary>
/// An analog touch pad.
/// </summary>
Input2DTouch,
/// <summary>
/// A digital directional pad.
/// </summary>
DPad
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2464bab5f214391a1671ba46c5dea20
timeCreated: 1643101364

View File

@@ -0,0 +1,37 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrFingerContactInfo.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
/// <summary>
/// Finger contact information. Stores information about where a finger touched a controller element.
/// </summary>
public class UxrFingerContactInfo
{
#region Public Types & Data
/// <summary>
/// Gets or sets the transform the finger is currently touching.
/// </summary>
public Transform Transform { get; set; }
#endregion
#region Constructors & Finalizer
/// <summary>
/// Constructor
/// </summary>
/// <param name="transform">The transform of where it made contact</param>
public UxrFingerContactInfo(Transform transform)
{
Transform = transform;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fa0c647e60bf4a8ebbe73fdc73005d85
timeCreated: 1643102273

View File

@@ -0,0 +1,255 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrMapControllerToHand.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections;
using UltimateXR.Avatar;
using UltimateXR.Core;
using UltimateXR.Core.Components.Composite;
using UltimateXR.Manipulation;
using UnityEngine;
namespace UltimateXR.Devices.Visualization
{
/// <summary>
/// UltimateXR has support for rendering avatars in 'controllers mode' (where the currently
/// active controllers are rendered with optional hands on top) or full Avatar mode, where the actual
/// visual representation of the user is rendered.
/// 'Controllers mode' can be useful during tutorials or menus while the full Avatar mode will be used
/// mainly for the core game/simulation.
/// When we developed the big example scene we included a small gallery with all supported controllers
/// so the user could grab and inspect them. But there was no link between the user input and how the
/// hands and the controllers behaved (like the 'controllers mode').
/// Since one of the coolest UltimateXR features is mapping user input to controllers and having IK drive
/// the hands we also wanted to link the grabbing mechanics to this. In short, what we did was to add
/// UxrControllerHand components to the regular avatar hands as well, and then at runtime link them
/// to the currently grabbed controller and feed the avatar input. This way we are now able to not
/// just grab to controller, but also see how the hand and the controller respond to the user input.
/// </summary>
[RequireComponent(typeof(UxrGrabbableObject))]
public class UxrMapControllerToHand : UxrGrabbableObjectComponent<UxrMapControllerToHand>
{
#region Inspector Properties/Serialized Fields
[SerializeField] private bool _ambidextrous;
#endregion
#region Unity
/// <summary>
/// Cache components.
/// </summary>
protected override void Start()
{
base.Start();
_controller3DModel = GetComponentInChildren<UxrController3DModel>();
}
#endregion
#region Coroutines
/// <summary>
/// Initializes the hand:
/// <list type="bullet"></list>
/// <item>
/// Block input mainly to prevent locomotion while pressing the controllers. We want the user to be able to move
/// the joysticks and press buttons without triggering unwanted events.
/// </item>
/// <item>
/// Use the current hand pose, which is used to grab the controller, to initialize the
/// <see cref="UxrControllerHand" /> component.
/// </item>
/// </summary>
/// <param name="grabber">Grabber that was used to grab the controller</param>
/// <param name="controllerHand">The controller hand component</param>
/// <returns>Coroutine IEnumerator</returns>
private IEnumerator InitializeHandCoroutine(UxrGrabber grabber, UxrControllerHand controllerHand)
{
yield return null;
controllerHand.InitializeFromCurrentHandPose(grabber.Avatar, grabber.Side);
if (grabber.Side == UxrHandSide.Left)
{
_ignoreInputLeft = UxrControllerInput.GetIgnoreControllerInput(UxrHandSide.Left);
UxrControllerInput.SetIgnoreControllerInput(UxrHandSide.Left, true);
}
else
{
_ignoreInputRight = UxrControllerInput.GetIgnoreControllerInput(UxrHandSide.Right);
UxrControllerInput.SetIgnoreControllerInput(UxrHandSide.Right, true);
}
}
#endregion
#region Event Handling Methods
/// <summary>
/// This callback will be executed at the end of all the UltimateXR frame pipeline when the user is grabbing the
/// controller. We feed both the controller and the hand's IK the user input.
/// </summary>
private void UxrManager_AvatarsUpdated()
{
_controller3DModel.UpdateFromInput(UxrAvatar.LocalAvatarInput);
if (_controller3DModel.ControllerHand)
{
_controller3DModel.ControllerHand.UpdateIKManually();
}
if (_controller3DModel.ControllerHandLeft)
{
_controller3DModel.ControllerHandLeft.UpdateIKManually();
}
if (_controller3DModel.ControllerHandRight)
{
_controller3DModel.ControllerHandRight.UpdateIKManually();
}
}
#endregion
#region Event Trigger Methods
/// <summary>
/// Called when the user grabs the controller. We bind the controller to the
/// hand that grabbed it and subscribe to events.
/// </summary>
/// <param name="e">Grab parameters</param>
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
{
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
UxrControllerHand controllerHand = GetControllerHand(e.Grabber);
if (controllerHand)
{
if (_controller3DModel.NeedsBothHands)
{
// Controller can be grabbed with both hands simultaneously
if (e.Grabber.Side == UxrHandSide.Left)
{
_controller3DModel.ControllerHandLeft = controllerHand;
}
else
{
_controller3DModel.ControllerHandRight = controllerHand;
}
}
else
{
// Some controllers are grabbed with one hand only but can be grabbed with both. Switch the component handedness
// if necessary to adapt to the hand that is grabbing it
if (_ambidextrous)
{
if (e.Grabber.Side != _controller3DModel.HandSide)
{
_controller3DModel.SwitchHandedness();
}
}
// Assign hand
_controller3DModel.ControllerHand = controllerHand;
}
// Initialize the hand using a coroutine so that it is performed the next frame.
StartCoroutine(InitializeHandCoroutine(e.Grabber, controllerHand));
controllerHand.enabled = true;
}
}
/// <inheritdoc />
protected override void OnObjectReleased(UxrManipulationEventArgs e)
{
OnControllerReleaseOrPlace(e);
}
/// <inheritdoc />
protected override void OnObjectPlaced(UxrManipulationEventArgs e)
{
OnControllerReleaseOrPlace(e);
}
/// <summary>
/// Called when the user releases or places this controller. We remove the connection
/// with the controller and the hand that grabbed it, and unsubscribe from events.
/// </summary>
/// <param name="e">Grab parameters</param>
private void OnControllerReleaseOrPlace(UxrManipulationEventArgs e)
{
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
UxrControllerHand controllerHand = GetControllerHand(e.Grabber);
if (controllerHand)
{
controllerHand.enabled = false;
if (_controller3DModel.NeedsBothHands)
{
if (e.Grabber.Side == UxrHandSide.Left)
{
_controller3DModel.ControllerHandLeft = null;
}
else
{
_controller3DModel.ControllerHandRight = null;
}
}
else
{
_controller3DModel.ControllerHand = null;
}
// Restore blocked input
UxrControllerInput.SetIgnoreControllerInput(e.Grabber.Side, e.Grabber.Side == UxrHandSide.Left ? _ignoreInputLeft : _ignoreInputRight);
}
}
#endregion
#region Private Methods
/// <summary>
/// Gets the UxrControllerHand component that belongs to the grabber passed. This basically
/// just checks if it is the left or right one and looks for the UxrControllerHand component
/// in the hand bone object.
/// </summary>
/// <param name="grabber">The grabber we want to get the UxrControllerHand for</param>
/// <returns>UxrControllerHand or null if none was found</returns>
private UxrControllerHand GetControllerHand(UxrGrabber grabber)
{
if (grabber && grabber.Side == UxrHandSide.Left && grabber.Avatar != null)
{
return grabber.Avatar.LeftHandBone.GetComponent<UxrControllerHand>();
}
if (grabber && grabber.Side == UxrHandSide.Right && grabber.Avatar != null)
{
return grabber.Avatar.RightHandBone.GetComponent<UxrControllerHand>();
}
return null;
}
#endregion
#region Private Types & Data
private UxrController3DModel _controller3DModel;
private bool _ignoreInputLeft;
private bool _ignoreInputRight;
#endregion
}
}

View File

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