first commit

This commit is contained in:
2026-04-07 03:14:32 +03:00
commit b3992fec6b
1026 changed files with 366769 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using UnityEditor;
using System;
namespace VirtualGrasp.Scripts
{
/**
* MyVirtualGrasp is a customizable main tutorial component.
*
* MyVirtualGrasp inherits from VG_MainScript, which wraps the main communication functions of the VirtualGrasp API.
* VG_MainScript inherits from Monobehavior so you can use this as a component to a GameObject in Unity.
* All the API functions you want to use in your own scripts can be accessed through VG_Controller.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_myvirtualgrasp." + VG_Version.__VG_VERSION__ + ".html")]
public class MyVirtualGrasp : VG_MainScript
{
override public void Awake()
{
base.Awake(); // note: Awake can delete this component if there already is one.
if (this != null)
{
VG_Controller.Initialize();
}
}
override public void Update()
{
base.Update();
}
override public void FixedUpdate()
{
base.FixedUpdate();
}
void OnApplicationQuit()
{
SaveState();
}
void OnApplicationPause()
{
// If linux save state
var p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 6) || (p == 128))
SaveState();
}
void SaveState()
{
#if UNITY_EDITOR
if (m_graspDB != null)
{
string graspDBPath = AssetDatabase.GetAssetPath(m_graspDB);
VG_Controller.SaveState(graspDBPath);
AssetDatabase.ImportAsset(graspDBPath);
}
else
{
VG_Controller.SaveState(null);
}
#else
VG_Controller.SaveState(null);
#endif
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 919761dfc44922242b6616aa92bf7903
timeCreated: 1491831663
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using UnityEngine.Events;
#if VG_ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#else
using UnityEngine.XR;
#endif
namespace VirtualGrasp.Scripts
{
/**
* VG_AnimationDriver provides a generic animation driver to drive finger and object animations
* to achieve in-hand manipulation of articulated objects.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vganimationdriver." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_AnimationDriver : MonoBehaviour
{
[SerializeField, Tooltip("Which hand is the driver")]
public VG_HandSide m_handSide;
#if VG_ENABLE_INPUT_SYSTEM
[SerializeField, Tooltip("Which action drives this animation")]
private InputActionReference m_actionReference;
#else
[SerializeField]
private VG_VrButton button = VG_VrButton.TRIGGER;
private InputDevice handInputDevice;
#endif
[SerializeField, Tooltip("Input value range")]
private Vector2 m_inputRange = new Vector2(0f, 1f);
[SerializeField, Tooltip("Optional, if unassigned this transform will be used")]
private Transform m_interactableObject;
[Tooltip("Event driving animation from input")]
public UnityEvent<float> OnDriven = new UnityEvent<float>();
[Tooltip("Generic animation driver events")]
public UnityEvent OnEnabled = new UnityEvent();
[Tooltip("Generic animation driver events")]
public UnityEvent OnDisabled = new UnityEvent();
private float m_driveValue;
private bool m_isHoldingHandRemote = false;
void Awake()
{
if (this.m_interactableObject == null)
this.m_interactableObject = transform;
}
void OnEnable()
{
OnDriven.Invoke(0.0f);
OnEnabled.Invoke();
}
void OnDisable()
{
OnDisabled.Invoke();
}
void Start()
{
VG_Controller.OnObjectGrasped.AddListener(OnObjectInteractionChanged);
VG_Controller.OnObjectReleased.AddListener(OnObjectInteractionChanged);
#if VG_ENABLE_INPUT_SYSTEM
if(this.m_actionReference != null)
this.m_actionReference.action.Enable();
#endif
enabled = false;
}
private void OnObjectInteractionChanged(VG_HandStatus status)
{
if (status.m_selectedObject != m_interactableObject) return;
if (status.m_side != m_handSide) return;
this.m_isHoldingHandRemote = status.m_isRemote;
this.enabled = status.IsHolding();
}
private void Update()
{
float inputValue = 0;
#if VG_ENABLE_INPUT_SYSTEM
if(this.m_actionReference != null)
{
inputValue = this.m_actionReference.action.ReadValue<float>();
}
#else
if (m_handSide == VG_HandSide.LEFT)
this.handInputDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
else
this.handInputDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
if (!this.handInputDevice.TryGetFeatureValue(this.button == VG_VrButton.TRIGGER ?
CommonUsages.trigger : CommonUsages.grip, out inputValue))
{
Debug.LogError($"Could not read button {this.button} on device {this.handInputDevice}");
return;
}
#endif
if (this.m_isHoldingHandRemote == false)
{
this.m_driveValue = Mathf.InverseLerp(m_inputRange.x, m_inputRange.y, inputValue);
}
OnDriven.Invoke(this.m_driveValue);
}
/// <summary>
/// Drives animation from other components, rather than the input reference
/// </summary>
public void Drive(float driveValue)
{
this.m_driveValue = driveValue;
}
public float GetDriveValue() => this.m_driveValue;
}
}

View File

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

View File

@@ -0,0 +1,356 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using System.Collections.Generic;
using VirtualGrasp;
using UnityEngine.Events;
namespace VirtualGrasp.Scripts
{
/**
* VG_Assemble provides a tool to assemble / dissemble an object through VG_Articulation.
* The MonoBehavior provides a tutorial on the VG API functions VG_Controller.ChangeObjectJoint and RecoverObjectJoint.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vgassemble." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_Assemble : MonoBehaviour
{
public enum AxisType
{
[Tooltip("Not match any axis, i.e. no rotation match")]
NoAxis = 0,
[Tooltip("Match X axis")]
XAxis = 1,
[Tooltip("Match Y axis")]
YAxis = 2,
[Tooltip("Match Z axis")]
ZAxis = 3,
[Tooltip("Match X axis in both direction")]
XAxisSymmetric = 4,
[Tooltip("Match Y axis in both direction")]
YAxisSymmetric = 5,
[Tooltip("Match Z axis in both direction")]
ZAxisSymmetric = 6
}
[Tooltip("If this object will be reparented to the parent of the desired pose transform when it is assembled.")]
public bool m_assembleToParent = true;
[Tooltip("The target pose(s) of the assembled object (or assemble anchor if provided).")]
public List<Transform> m_desiredPoses = new List<Transform>();
[Tooltip("Threshold to assemble when object (or assemble anchor) to desired pose is smaller than this value (m).")]
public float m_assembleDistance = 0.05f;
[Tooltip("Threshold to assemble when object (or assemble anchor) to desired rotation is smaller than this value (deg).")]
public float m_assembleAngle = 45;
[Tooltip("The axis of the Assemble Anchor if assigned (otherwise of the object) to be aligned for assembling to a desired rotation. If NoAxis then no match to target rotation.")]
public AxisType m_assembleAxis = AxisType.YAxis;
[Tooltip("The number of discrete steps across 360 deg around Assemble Axis to match the rotation. 0 means rotational symmetric.")]
public int m_assembleSymmetrySteps = 0;
[Tooltip("If provided will match this anchor to the desired pose, otherwise match this object.")]
public Transform m_assembleAnchor = null;
[Tooltip("Threshold to disassemble when object (or assemble anchor) distance to desired pose is bigger than this value (m).")]
public float m_disassembleDistance = 0.25f;
[Tooltip("If only allow dissasemble when object is at the zero joint state (most relevant for screw joint).")]
public bool m_disassembleOnZeroState = false;
[Tooltip("The VG Articulation of constrained (non-FLOATING) joint type to switch to when assembling an object. If null will use Fixed joint.")]
public VG_Articulation m_assembleArticulation = null;
[Tooltip("The VG Articulation of floating joint type to switch to when disassembling an object. Must be provided if original joint is non-Floating.")]
public VG_Articulation m_disassembleArticulation = null;
[Tooltip("If force the disassembled object to become physical. Only relevant if original joint is non-Floating.")]
public bool m_forceDisassembledPhysical = false;
[Tooltip("Event triggered when object is to be assembled (satisfying assembling criterior but before joint and parent change), return the matched target transform.")]
public UnityEvent<Transform> OnBeforeAssembled = new UnityEvent<Transform>();
[Tooltip("Event triggered when object is assembled, return the assembled target transform.")]
public UnityEvent<Transform> OnAssembled = new UnityEvent<Transform>();
[Tooltip("Event triggered when object is disassembled, return the target transform from which the object is disassembled.")]
public UnityEvent<Transform> OnDisassembled = new UnityEvent<Transform>();
private float m_timeAtDisassemble = 0.0F;
private float m_assembleDelay = 2.0F;
public Transform m_disassembleParent = null;
private Transform m_desiredPose = null;
void Start()
{
if (m_assembleArticulation == null)
Debug.LogWarning("Assemble Articulation is not assigned, so assemble will use Fixed joint for " + transform.name, transform);
else if (m_assembleArticulation.m_type == VG_JointType.FLOATING)
{
Debug.LogError(transform.name + "'s Assemble Articulation can not be FLOATING joint type.", transform);
this.enabled = false;
}
if(VG_Controller.GetObjectJointType(transform, true, out VG_JointType jointType) == VG_ReturnCode.SUCCESS && jointType == VG_JointType.FLOATING
&& m_disassembleParent == null)
m_disassembleParent = transform.parent;
if (VG_Controller.GetObjectJointType(transform, true, out VG_JointType originalJointType) == VG_ReturnCode.SUCCESS
&& originalJointType != VG_JointType.FLOATING)
{
if (m_disassembleArticulation == null)
{
Debug.LogError(transform.name + "'s initial joint is constrained type " + originalJointType + ", Disassemble Articulation with FLOATING joint type needs to be assigned.", transform);
this.enabled = false;
}
else if (m_disassembleArticulation.m_type != VG_JointType.FLOATING)
{
Debug.LogError(transform.name + "'s Disassemble Articulation has to be FLOATING joint type.", transform);
this.enabled = false;
}
// If originally is non-floating means disassemble parent should be original parent's parent
if(m_disassembleParent == null)
m_disassembleParent = transform.parent.parent;
}
if (m_assembleAnchor == null)
m_assembleAnchor = transform;
}
public void SetTargetTransformActive(bool active)
{
if(m_desiredPose != null)
m_desiredPose.gameObject.SetActive(active);
}
void LateUpdate()
{
assembleByJointChange();
disassembleByJointChange();
}
void assembleByJointChange()
{
Quaternion relRot = Quaternion.identity;
if (!findTarget(ref relRot))
return;
VG_JointType jointType;
if ((Time.timeSinceLevelLoad - m_timeAtDisassemble) > m_assembleDelay
&& VG_Controller.GetObjectJointType(transform, false, out jointType) == VG_ReturnCode.SUCCESS &&
jointType == VG_JointType.FLOATING)
{
OnBeforeAssembled.Invoke(m_desiredPose);
// Project object rotation axis to align to desired rotation axis.
transform.SetPositionAndRotation(transform.position, relRot * transform.rotation);
Vector3 offset = m_desiredPose.position - m_assembleAnchor.position;
transform.SetPositionAndRotation(transform.position + offset, transform.rotation);
if (m_assembleToParent)
transform.SetParent(m_desiredPose.parent);
VG_ReturnCode ret = m_assembleArticulation ? VG_Controller.ChangeObjectJoint(transform, m_assembleArticulation) : VG_Controller.ChangeObjectJoint(transform, VG_JointType.FIXED);
if (ret != VG_ReturnCode.SUCCESS)
Debug.LogError("Failed to ChangeObjectJoint() on " + transform.name + " with return code " + ret, transform);
OnAssembled.Invoke(m_desiredPose);
}
}
void disassembleByJointChange()
{
// When the object with simulated weight is in "heaving lifting" phase, disallow distance-based disassemble
if (VG_Controller.IsLiftingObject(transform))
return;
foreach (VG_HandStatus hand in VG_Controller.GetHands())
{
VG_JointType jointType;
if (hand.m_selectedObject == transform && hand.IsHolding()
&& VG_Controller.GetObjectJointType(transform, false, out jointType) == VG_ReturnCode.SUCCESS
&& jointType != VG_JointType.FLOATING)
{
getSensorControlledAnchorPose(hand, out Vector3 sensor_anchor_pos, out Quaternion sensor_anchor_rot);
if (isZeroState(jointType)
&& (sensor_anchor_pos - m_assembleAnchor.position).magnitude > m_disassembleDistance
)
{
if (m_assembleToParent || m_disassembleParent == null)
transform.SetParent(transform.parent.parent);
else
transform.SetParent(m_disassembleParent);
VG_Controller.GetObjectJointType(transform, true, out VG_JointType originalJointType);
if (originalJointType == VG_JointType.FLOATING)
{
if (VG_Controller.RecoverObjectJoint(transform) != VG_ReturnCode.SUCCESS)
{
Debug.LogError("Failed to disassemble with RecoverObjectJoint() on " + transform.name);
return;
}
}
else if (m_disassembleArticulation != null)
{
if (VG_Controller.ChangeObjectJoint(transform, m_disassembleArticulation) != VG_ReturnCode.SUCCESS)
{
Debug.LogError("Failed to disassemble with ChangeObjectJoint() on " + transform.name);
return;
}
// When object originally has VG constrained joint type (non-Floating) it has to be a non-physical object,
// therefore disassemble will not recover its original physical property, so here we add rigid body
// if user choose to m_makeDisassembledPhysical.
if (m_forceDisassembledPhysical && !transform.gameObject.TryGetComponent<Rigidbody>(out Rigidbody rb))
{
rb = transform.gameObject.AddComponent<Rigidbody>();
rb.useGravity = true;
if (!transform.TryGetComponent<Collider>(out _))
{
MeshCollider collider = transform.gameObject.AddComponent<MeshCollider>();
collider.convex = true;
}
}
}
else
{
Debug.LogError("Failed to disassemble with ChangeObjectJoint() since Disassemble Articulation on " + transform.name + " is not assigned.");
return;
}
m_timeAtDisassemble = Time.timeSinceLevelLoad;
OnDisassembled.Invoke(m_desiredPose);
}
}
}
}
bool isZeroState(VG_JointType jointType)
{
if (!m_disassembleOnZeroState)
return true;
// If object is of a joint type that has no relevant joint states, then no zero state control for disassemble so return true
VG_Controller.GetObjectJointState(transform, out float jointState);
if (jointType == VG_JointType.REVOLUTE || jointType == VG_JointType.PRISMATIC || jointType == VG_JointType.CONE)
return jointState == 0.0F;
else if (jointType == VG_JointType.PLANAR)
{
VG_Controller.GetObjectSecondaryJointState(transform, out float jointState2);
return (jointState == 0 && jointState2 == 0);
}
else
return true;
}
void getSensorControlledAnchorPose(VG_HandStatus hand, out Vector3 anchorPos, out Quaternion anchorRot)
{
// Compute relative pose of anchor to grasping hand pose
Vector3 lp = Quaternion.Inverse(hand.m_hand.rotation) * (m_assembleAnchor.position - hand.m_hand.position);
Quaternion lq = Quaternion.Inverse(hand.m_hand.rotation) * m_assembleAnchor.rotation;
// Then evaluate anchor rotation corresponding to hand pose determined by sensor
VG_Controller.GetSensorPose(hand.m_avatarID, hand.m_side, out Vector3 sensor_pos, out Quaternion sensor_rot);
anchorPos = sensor_rot * lp + sensor_pos;
anchorRot = sensor_rot * lq;
}
bool findTarget(ref Quaternion relRot)
{
// If object is already assembled, don't need to find target.
if (VG_Controller.GetObjectJointType(transform, false, out VG_JointType jointType) == VG_ReturnCode.SUCCESS
&& jointType != VG_JointType.FLOATING)
return false;
m_desiredPose = null;
foreach (Transform pose in m_desiredPoses)
{
if (closeToTargetPose(m_assembleAnchor, pose, m_assembleAxis, m_assembleSymmetrySteps, ref relRot))
{
m_desiredPose = pose;
return true;
}
}
return false;
}
bool closeToTargetPose(Transform anchor, Transform target, AxisType assembleAxis, int angleSteps, ref Quaternion relRot)
{
return (target.position - anchor.position).magnitude < m_assembleDistance &&
closeToTargetRotation(anchor, target, assembleAxis, angleSteps, ref relRot);
}
/// <summary>
/// Check if rotationally close to target and compute relRot to rotate anchor to target
/// </summary>
/// <param name="anchor">Anchor whose rotation is to match to target</param>
/// <param name="target">Target to map anchor rotation to</param>
/// <param name="assembleAxis">Axis type to map rotation on a plane defined by this</param>
/// <param name="angleSteps">number of steps of angles to define a set of evenly distributed axes on the plane defined by assembleAxis</param>
/// <param name="relRot">Output, the relative rotation to apply to anchor to map to target</param>
/// <returns></returns>
bool closeToTargetRotation(Transform anchor, Transform target, AxisType assembleAxis, int angleSteps, ref Quaternion relRot)
{
relRot = Quaternion.identity;
if (assembleAxis == AxisType.NoAxis)
return true;
float angle = 0.0F;
Quaternion relRot2 = Quaternion.identity;
float angle2 = 0.0F;
switch (assembleAxis)
{
case AxisType.XAxis:
case AxisType.XAxisSymmetric:
computeRelativeRotationAngle(anchor.right, target.right, target.forward, (assembleAxis == AxisType.XAxisSymmetric)? 2 : 1, ref relRot, ref angle);
if(angleSteps >0)
computeRelativeRotationAngle((relRot * anchor.rotation) * Vector3.up, target.up, target.right, angleSteps, ref relRot2, ref angle2);
relRot = relRot2 * relRot;
break;
case AxisType.YAxis:
case AxisType.YAxisSymmetric:
computeRelativeRotationAngle(anchor.up, target.up, target.forward, (assembleAxis == AxisType.YAxisSymmetric) ? 2 : 1, ref relRot, ref angle);
if(angleSteps >0)
computeRelativeRotationAngle((relRot * anchor.rotation) * Vector3.right, target.right, target.up, angleSteps, ref relRot2, ref angle2);
relRot = relRot2 * relRot;
break;
case AxisType.ZAxis:
case AxisType.ZAxisSymmetric:
computeRelativeRotationAngle(anchor.forward, target.forward, target.right, (assembleAxis == AxisType.ZAxisSymmetric) ? 2 : 1, ref relRot, ref angle);
if(angleSteps >0)
computeRelativeRotationAngle((relRot * anchor.rotation) * Vector3.right, target.right, target.forward, angleSteps, ref relRot2, ref angle2);
relRot = relRot2 * relRot;
break;
default:
break;
}
return (angle < m_assembleAngle && angle2 < m_assembleAngle);
}
/// <summary>
/// Compute relative rotation and angle from anchor axis to a set of axis starting form target axis following a number of steps, will find smallest relRot / angle
/// </summary>
/// <param name="anchorAxis">The axis to rotate to one of the targetAxis</param>
/// <param name="targetAxis">The target axis as starting axis to rotate to</param>
/// <param name="axis">The axis orthorganal to targetAxis around which will rotate a step from starting targetAxis</param>
/// <param name="angleSteps">The number of steps around 360 deg to rotate target axis, if 0 means rotational symmetric</param>
/// <param name="relRot">Output, the minimum relative rotation to map anchor axis to target axis</param>
/// <param name="angle">Output, angle corresponding to relRot</param>
void computeRelativeRotationAngle(Vector3 anchorAxis, Vector3 targetAxis, Vector3 axis, int angleSteps, ref Quaternion relRot, ref float angle)
{
relRot = Quaternion.identity;
angle = 360.0f;
Quaternion rot = Quaternion.AngleAxis(360.0F / angleSteps, axis);
float rel_angle = 0.0f;
Quaternion rel_rot = Quaternion.identity;
for (int i = 0; i < angleSteps; i++)
{
rel_rot = Quaternion.FromToRotation(anchorAxis, targetAxis);
rel_rot.ToAngleAxis(out rel_angle, out _);
if (rel_angle < angle)
{
angle = rel_angle;
relRot = rel_rot;
}
targetAxis = rot * targetAxis;
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,86 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports a Mouse controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_cp_generic." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Generic : VG_ExternalController
{
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
{ 0, Hand_WristRoot },
{ 1, Hand_Thumb1 },
{ 2, Hand_Thumb2 },
{ 3, Hand_Thumb3 },
{ 4, Hand_Index1 },
{ 5, Hand_Index2 },
{ 6, Hand_Index3 },
{ 7, Hand_Middle1 },
{ 8, Hand_Middle2 },
{ 9, Hand_Middle3 },
{ 10, Hand_Ring1 },
{ 11, Hand_Ring2 },
{ 12, Hand_Ring3 },
{ 13, Hand_Pinky1 },
{ 14, Hand_Pinky2 },
{ 15, Hand_Pinky3 }
};
}
}
public VG_EC_Generic(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_zeroOffsets = true; // the generic hand works on the Unity transforms, so it can't use offsets.
Initialize();
}
public new void Initialize()
{
m_mapping = new HandMapping();
base.Initialize();
m_enabled = true;
}
public override bool Compute()
{
for (int bone = 0; bone < m_mapping.GetNumBones(); bone++)
{
if (!m_mapping.GetTransform(bone, out Transform pose)) continue;
SetPose(bone, Matrix4x4.TRS(pose.position, pose.rotation, Vector3.one));
}
return true;
}
public override float GetGrabStrength()
{
return 0.0f;
}
public override Color GetConfidence()
{
return Color.yellow;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
}
}
}

View File

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

View File

@@ -0,0 +1,205 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_LEAP_CONTROLLER
using System;
using System.Collections.Generic;
using UnityEngine;
#if VG_USE_LEAP_CONTROLLER
using Leap.Unity;
#endif
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the LeapMotion controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_LEAP_CONTROLLER above and use the controller:
* - You have a Core Assets plugin from https://developer.leapmotion.com/unity imported into your Unity project.
* - Note that Core Assets > 4.4.0 are for LeapMotion SDK 4, older are for LeapMotion SDK 3 (lastest CA 4.3.4).
* - You have the corresponding LeapMotion SDK (https://developer.leapmotion.com/sdk-leap-motion-controller/) installed on your computer.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_leap." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Leap : VG_ExternalController
{
#if VG_USE_LEAP_CONTROLLER
static LeapProvider m_provider = null;
Leap.Hand m_hand = null;
static public int LeapBoneToInt(int finger, int bone)
{
return 4 * finger + bone + 1;
}
static public int LeapBoneToInt(Leap.Finger.FingerType finger, Leap.Bone.BoneType bone)
{
return LeapBoneToInt((int)finger, (int)bone);
}
static public void IntToLeapBone(int id, out int finger, out Leap.Bone.BoneType bone)
{
bone = (Leap.Bone.BoneType)((id - 1) % 4);
finger = (id - (int)bone) / 4;
}
#endif
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
#if VG_USE_LEAP_CONTROLLER
{ 0, Hand_WristRoot },
{ LeapBoneToInt(0, 0), null },
{ LeapBoneToInt(0, 1), Hand_Thumb1 },
{ LeapBoneToInt(0, 2), Hand_Thumb2 },
{ LeapBoneToInt(0, 3), Hand_Thumb3 },
{ LeapBoneToInt(1, 0), null },
{ LeapBoneToInt(1, 1), Hand_Index1 },
{ LeapBoneToInt(1, 2), Hand_Index2 },
{ LeapBoneToInt(1, 3), Hand_Index3 },
{ LeapBoneToInt(2, 0), null },
{ LeapBoneToInt(2, 1), Hand_Middle1 },
{ LeapBoneToInt(2, 2), Hand_Middle2 },
{ LeapBoneToInt(2, 3), Hand_Middle3 },
{ LeapBoneToInt(3, 0), null },
{ LeapBoneToInt(3, 1), Hand_Ring1 },
{ LeapBoneToInt(3, 2), Hand_Ring2 },
{ LeapBoneToInt(3, 3), Hand_Ring3 },
{ LeapBoneToInt(4, 0), null },
{ LeapBoneToInt(4, 1), Hand_Pinky1 },
{ LeapBoneToInt(4, 2), Hand_Pinky2 },
{ LeapBoneToInt(4, 3), Hand_Pinky3 }
#endif
};
m_BoneToParent = new Dictionary<int, int>()
{
};
#if VG_USE_LEAP_CONTROLLER
m_BoneToParent[LeapBoneToInt(0, 0)] = 0;
m_BoneToParent[LeapBoneToInt(0, 1)] = LeapBoneToInt(0, 0);
m_BoneToParent[LeapBoneToInt(0, 2)] = LeapBoneToInt(0, 1);
m_BoneToParent[LeapBoneToInt(0, 3)] = LeapBoneToInt(0, 2);
m_BoneToParent[LeapBoneToInt(1, 0)] = 0;
m_BoneToParent[LeapBoneToInt(1, 1)] = LeapBoneToInt(1, 0);
m_BoneToParent[LeapBoneToInt(1, 2)] = LeapBoneToInt(1, 1);
m_BoneToParent[LeapBoneToInt(1, 3)] = LeapBoneToInt(1, 2);
m_BoneToParent[LeapBoneToInt(2, 0)] = 0;
m_BoneToParent[LeapBoneToInt(2, 1)] = LeapBoneToInt(2, 0);
m_BoneToParent[LeapBoneToInt(2, 2)] = LeapBoneToInt(2, 1);
m_BoneToParent[LeapBoneToInt(2, 3)] = LeapBoneToInt(2, 2);
m_BoneToParent[LeapBoneToInt(3, 0)] = 0;
m_BoneToParent[LeapBoneToInt(3, 1)] = LeapBoneToInt(3, 0);
m_BoneToParent[LeapBoneToInt(3, 2)] = LeapBoneToInt(3, 1);
m_BoneToParent[LeapBoneToInt(3, 3)] = LeapBoneToInt(3, 2);
m_BoneToParent[LeapBoneToInt(4, 0)] = 0;
m_BoneToParent[LeapBoneToInt(4, 1)] = LeapBoneToInt(4, 0);
m_BoneToParent[LeapBoneToInt(4, 2)] = LeapBoneToInt(4, 1);
m_BoneToParent[LeapBoneToInt(4, 3)] = LeapBoneToInt(4, 2);
#endif
}
}
public VG_EC_Leap(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enablingDefine = "VG_USE_LEAP_CONTROLLER";
#if VG_USE_LEAP_CONTROLLER
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public new void Initialize()
{
#if VG_USE_LEAP_CONTROLLER
if (m_provider == null)
{
m_provider = GameObject.FindObjectOfType<VG_MainScript>().gameObject.AddComponent<LeapServiceProvider>();
}
if (m_provider != null)
{
m_mapping = new HandMapping();
base.Initialize();
}
m_initialized = (m_provider != null);
#endif
}
public override bool Compute()
{
#if VG_USE_LEAP_CONTROLLER
if (!m_enabled) return false;
if (!m_initialized || m_mapping == null) { Initialize(); return false; }
if (m_provider == null) return false;
Leap.Frame frame = m_provider.CurrentFrame;
if (frame == null) return false;
m_hand = null;
foreach (Leap.Hand hand in frame.Hands)
{
if (hand.IsLeft && m_handType == VG_HandSide.LEFT) { m_hand = hand; break; }
if (!hand.IsLeft && m_handType == VG_HandSide.RIGHT) { m_hand = hand; break; }
}
if (m_hand == null) return false;
for (int boneId = 0; boneId < GetNumBones(); boneId++)
{
if (boneId == 0)
{
SetPose(boneId, Matrix4x4.TRS(m_hand.WristPosition, m_hand.Rotation, Vector3.one));
}
else
{
IntToLeapBone(boneId, out int finger, out Leap.Bone.BoneType bone);
Leap.Bone b = m_hand.Fingers[finger].Bone(bone);
SetPose(boneId, Matrix4x4.TRS(b.NextJoint, b.Rotation, Vector3.one));
}
}
return true;
#else
return false;
#endif
}
public override float GetGrabStrength()
{
#if VG_USE_LEAP_CONTROLLER
// Get grab strength from Leap if available
//return m_hand != null ? m_hand.GrabStrength : 0.0f;
return -1.0f; // let VG decide from full DOF
#else
return 0.0f;
#endif
}
public override Color GetConfidence()
{
#if VG_USE_LEAP_CONTROLLER
if (m_hand != null) return Color.black;
return m_hand.Confidence > 0.5f ? Color.green : Color.red;
#else
return Color.yellow;
#endif
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
}
}
}

View File

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

View File

@@ -0,0 +1,413 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_MANUS_CONTROLLER
using System;
using System.Collections.Generic;
using UnityEngine;
#if VG_USE_MANUS_CONTROLLER
using System.Linq;
using Manus;
using Manus.Skeletons;
using Manus.Utility;
#endif
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the Manus Glove controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_MANUS_CONTROLLER above and use the controller:
* - You have the corresponding Manus Core SDK (https://resources.manus-meta.com/downloads) installed on your computer.
* - You have the Unity Plugin for Manus Core from https://resources.manus-meta.com/downloads imported into your Unity project.
* - You have a Manus Pro License assigned to your SDK to use the Unity Plugin.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_manus." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Manus : VG_ExternalController
{
#if VG_USE_MANUS_CONTROLLER
private Skeleton m_skeleton = null;
private CoreSDK.SkeletonStream m_skeletonData;
private int m_id = -1;
private static uint MANUS_SKELETON_ID = 0;
private void onSkeletonData(CoreSDK.SkeletonStream data)
{
m_skeletonData = data;
}
protected void IntFromManusBone(uint nodeId, out int boneId)
{
boneId = (int)nodeId;
}
static private void PrintNode(CoreSDK.SkeletonNode node)
{
Debug.Log("CNode " + node.ToString());
}
static private void PrintNode(Node node)
{
Debug.Log("Node " + node.id + " (" + node.name + "|" + node.nodeName + ")" +
node.unityTransform.name + " (type: " + node.type + "; parent: " + node.parentID + ")"
);
}
static private void PrintChain(Chain chain)
{
string str = "";
foreach (uint id in chain.nodeIds)
str += id + ",";
Debug.Log("Chain " + chain.id + " (" + chain.name + "|" + chain.dataSide + ")"
+ " (type: " + chain.type + "; hand: " + chain.settings.finger.handChainId + "); meta " + chain.settings.finger.metacarpalBoneId
+ "; " + str);
}
#endif
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
#if VG_USE_MANUS_CONTROLLER
{ 0, Hand_WristRoot },
{ 1, Hand_Thumb1 },
{ 2, Hand_Thumb2 },
{ 3, Hand_Thumb3 },
{ 4, null },
{ 5, null },
{ 6, Hand_Index1 },
{ 7, Hand_Index2 },
{ 8, Hand_Index3 },
{ 9, null },
{ 10, null },
{ 11, Hand_Middle1 },
{ 12, Hand_Middle2 },
{ 13, Hand_Middle3 },
{ 14, null },
{ 15, null },
{ 16, Hand_Ring1 },
{ 17, Hand_Ring2 },
{ 18, Hand_Ring3 },
{ 19, null },
{ 20, null },
{ 21, Hand_Pinky1 },
{ 22, Hand_Pinky2 },
{ 23, Hand_Pinky3 },
{ 24, null },
#endif
};
m_BoneToParent = new Dictionary<int, int>()
{
};
#if VG_USE_MANUS_CONTROLLER
m_BoneToParent[0] = -1;
m_BoneToParent[1] = 0;
m_BoneToParent[2] = 1;
m_BoneToParent[3] = 2;
m_BoneToParent[4] = 3;
m_BoneToParent[5] = 0;
m_BoneToParent[6] = 5;
m_BoneToParent[7] = 6;
m_BoneToParent[8] = 7;
m_BoneToParent[9] = 8;
m_BoneToParent[10] = 0;
m_BoneToParent[11] = 10;
m_BoneToParent[12] = 11;
m_BoneToParent[13] = 12;
m_BoneToParent[14] = 13;
m_BoneToParent[15] = 0;
m_BoneToParent[16] = 15;
m_BoneToParent[17] = 16;
m_BoneToParent[18] = 17;
m_BoneToParent[19] = 18;
m_BoneToParent[20] = 0;
m_BoneToParent[21] = 20;
m_BoneToParent[22] = 21;
m_BoneToParent[23] = 22;
m_BoneToParent[24] = 23;
#endif
}
#if VG_USE_MANUS_CONTROLLER
// Function to setup nodes for metacarpals and tips properly in Manus SDK.
// (They are not considered in VG controllers.)
private void FillUnassignedNodes(List<Node> nodes)
{
for (int nodeId = 0; nodeId < nodes.Count; nodeId++)
{
Node node = nodes[nodeId];
if (node.unityTransform == null)
{
// metacarpals
if (new List<int>() { 5, 10, 15, 20 }.Contains(nodeId))
{
Node next_node = nodes[nodeId + 1];
node.unityTransform = next_node.unityTransform.parent;
node.nodeName = (node.unityTransform == null) ? "n/a" : node.unityTransform.name;
node.name = node.nodeName;
}
else // tips
{
Node prev_node = nodes[nodeId - 1];
node.unityTransform = prev_node.unityTransform.GetChild(0);
node.nodeName = (node.unityTransform == null) ? "n/a" : node.unityTransform.name;
node.name = node.nodeName;
}
}
}
}
private bool ComputeFingerChainIds(out List<uint> chainIds)
{
chainIds = new List<uint>();
foreach (Transform child in Hand_WristRoot.transform)
{
List<Transform> children = child.GetComponentsInChildren<Transform>().ToList<Transform>();
if (children.Contains(Hand_Thumb1))
chainIds.Add(2);
else if (children.Contains(Hand_Index1))
chainIds.Add(3);
else if (children.Contains(Hand_Middle1))
chainIds.Add(4);
else if (children.Contains(Hand_Ring1))
chainIds.Add(5);
else if (children.Contains(Hand_Pinky1))
chainIds.Add(6);
// Check if we have properly ordered fingers.
int count = chainIds.Count;
if (count > 1 && chainIds[count - 1] < chainIds[count - 2])
return false;
}
return true;
}
public bool FillSkeletonFromMapping(Skeleton skeleton, VG_HandSide side)
{
skeleton.skeletonData = new SkeletonData();
skeleton.skeletonData.type = CoreSDK.SkeletonType.Hand;
skeleton.skeletonData.name = skeleton.name;
skeleton.skeletonData.settings.targetType = CoreSDK.SkeletonTargetType.UserIndexData;
skeleton.skeletonData.nodes.Clear();
skeleton.skeletonData.chains.Clear();
// Hand chain
Chain chain1 = new Chain();
chain1.id = 1;
chain1.type = CoreSDK.ChainType.Hand;
chain1.appliedDataType = chain1.type;
chain1.dataSide = (side == VG_HandSide.LEFT) ? CoreSDK.Side.Left : CoreSDK.Side.Right;
chain1.nodeIds.Add(0);
chain1.settings.finger.handChainId = 0;
chain1.settings.finger.metacarpalBoneId = 0;
chain1.settings.hand.fingerChainIds = new[] { 2, 3, 4, 5, 6 };
skeleton.skeletonData.chains.Add(chain1);
// Add all nodes from VG mapping.
foreach (KeyValuePair<int, Transform> boneTransform in m_BoneToTransform)
{
Node node = new Node();
node.unityTransform = boneTransform.Value;
node.nodeName = (node.unityTransform == null) ? "n/a" : node.unityTransform.name;
node.name = node.nodeName;
node.type = CoreSDK.NodeType.Joint;
node.id = (uint)boneTransform.Key;
node.parentID = m_BoneToParent.ContainsKey(boneTransform.Key) ?
(uint)m_BoneToParent[boneTransform.Key] : 0;
node.transform = new TransformValues();
node.transform.scale = Vector3.one;
skeleton.skeletonData.nodes.Add(node);
}
// Fill in nodes for bones that don't exist in VG skeleton
FillUnassignedNodes(skeleton.skeletonData.nodes);
// Finger chains (chain to bones)
// Note: the chains need to be exactly ordered as in the hierarchy.
if (!ComputeFingerChainIds(out List<uint> chainIds))
{
Debug.LogError($"Please assure that the finger chains (children of {Hand_WristRoot.name}) are sorted as:" +
"(Thumb, Index, Middle, Ring, Pinky)", Hand_WristRoot);
return false;
}
foreach (uint chainId in chainIds)
{
Chain chain = new Chain();
chain.name = "Finger " + chainId;
chain.id = chainId;
chain.type = (CoreSDK.ChainType)chainId + 3;
chain.appliedDataType = chain.type;
chain.dataSide = (side == VG_HandSide.LEFT) ? CoreSDK.Side.Left : CoreSDK.Side.Right;
chain.nodeIds = new List<uint>();
chain.settings.finger.handChainId = 1;
if (chainId == 2) // thumb
{
chain.settings.finger.metacarpalBoneId = -1;
chain.nodeIds = new List<uint> { 1, 2, 3, 4 };
}
else
{
chain.settings.finger.metacarpalBoneId = (int)(5 * (chainId - 2));
chain.nodeIds = new List<uint>();
for (int i = chain.settings.finger.metacarpalBoneId; i < chain.settings.finger.metacarpalBoneId + 5; i++)
chain.nodeIds.Add((uint)i);
}
skeleton.skeletonData.chains.Add(chain);
}
/*
// Just for debugging
foreach (Node node in skeleton.skeletonData.nodes)
PrintNode(node);
foreach (Chain chain in skeleton.skeletonData.chains)
PrintChain(chain);
*/
return true;
}
#endif
}
public VG_EC_Manus(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enablingDefine = "VG_USE_MANUS_CONTROLLER";
#if VG_USE_MANUS_CONTROLLER
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public new void Initialize()
{
#if VG_USE_MANUS_CONTROLLER
if (m_mapping == null)
{
m_mapping = new HandMapping();
base.Initialize();
}
// Note: need to disable GO first so Skeleton constructor does not fail
// due to uninitialized data structures (FillSkeletonFromMapping).
GameObject go = m_mapping.Hand_WristRoot.gameObject;
bool oldActive = go.activeSelf;
go.SetActive(false);
if (!go.TryGetComponent<Skeleton>(out m_skeleton))
m_skeleton = go.AddComponent<Skeleton>();
// Note: paused needs to be modified to public so Manus API does not apply bone poses,
// but let's VG do this.
m_skeleton.m_Paused = true;
m_initialized = false;
if (!(m_mapping as HandMapping).FillSkeletonFromMapping(m_skeleton, m_handType))
{
this.m_enabled = false;
return;
}
try { go.SetActive(oldActive); }
catch (Exception) { return; }
// Note: ManusManager is static and managing the gloves
m_skeleton.SetupMeshes();
m_skeleton.SetupNodes();
m_skeleton.SendSkeleton();
m_skeleton.skeletonData.id = MANUS_SKELETON_ID++;
m_id = (int)m_skeleton.skeletonData.id;
ManusManager.communicationHub?.onSkeletonData.AddListener(this.onSkeletonData);
m_initialized = true;
#endif
}
public override bool Compute()
{
#if VG_USE_MANUS_CONTROLLER
if (!m_enabled) return false;
if (!m_initialized || m_mapping == null) { Initialize(); return false; }
if (m_skeletonData.skeletons == null || m_skeletonData.skeletons.Count == 0) return false;
foreach (CoreSDK.SkeletonNode node in m_skeletonData.skeletons[m_id].nodes)
{
IntFromManusBone(node.id, out int boneId);
//Debug.Log("VG " + m_handType + ":" + m_id + " (" + boneId + "/" + GetNumBones() + "); MAN: " + node.id + "/" + m_skeletonData.skeletons[0].nodes.Length + "):\n" + node.transform.rotation.FromManus());
if (boneId == 0)
{
SetPose(boneId, Matrix4x4.TRS(
node.transform.position.FromManus(),
node.transform.rotation.FromManus().normalized,
Vector3.one));
}
else
{
SetPose(boneId, m_poses[m_mapping.GetParent(boneId)] *
Matrix4x4.TRS(
node.transform.position.FromManus(),
node.transform.rotation.FromManus().normalized,
Vector3.one));
}
}
return true;
#else
return false;
#endif
}
public override float GetGrabStrength()
{
#if VG_USE_MANUS_CONTROLLER
// No grab strength from Manus API is available, so
// let VG decide from full DOF.
return -1.0f;
#else
return 0.0f;
#endif
}
public override Color GetConfidence()
{
#if VG_USE_MANUS_CONTROLLER
// No confidence value of the tracked data in Manus API.
return Color.yellow;
#else
return Color.yellow;
#endif
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
#if VG_USE_MANUS_CONTROLLER
float[] amplitudes = new float[] { amplitude, amplitude, amplitude, amplitude, amplitude};
ManusManager.communicationHub?.SendHapticDataForSkeleton(m_skeleton.skeletonData.id, m_handType == VG_HandSide.LEFT ? CoreSDK.Side.Left : CoreSDK.Side.Right, amplitudes);
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,109 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System;
using System.Collections.Generic;
using UnityEngine;
#if VG_ENABLE_INPUT_SYSTEM // && !ENABLE_LEGACY_INPUT_MANAGER
using UnityEngine.InputSystem;
#endif
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports a Mouse controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_mouse." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Mouse : VG_ExternalController
{
private int mouse_held = 0;
private int filter = 15;
private float depth = .5f;
private Vector3 rotation = Vector3.zero;
#if VG_ENABLE_INPUT_SYSTEM
private float rotationSpeed = 0.5f;
#endif
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
{ 0, Hand_WristRoot }
};
}
}
public VG_EC_Mouse(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enabled = true;
}
public new void Initialize()
{
m_initialized = (Camera.main != null);
if (!m_initialized) return;
m_mapping = new HandMapping();
base.Initialize();
if (Camera.main.stereoTargetEye != StereoTargetEyeMask.None)
{
Debug.LogWarning("VG_EC_MouseHand uses single GameView camera, but a stereo camera is activated. Deactivating Stereo view.");
Camera.main.stereoTargetEye = StereoTargetEyeMask.None;
}
}
public override bool Compute()
{
if (!m_enabled || !Application.isFocused) return false;
if (!m_initialized || m_mapping == null) { Initialize(); return false; }
#if VG_ENABLE_INPUT_SYSTEM // && !ENABLE_LEGACY_INPUT_MANAGER
if (Keyboard.current.leftShiftKey.isPressed && m_handType == VG_HandSide.RIGHT ||
Keyboard.current.rightShiftKey.isPressed && m_handType == VG_HandSide.LEFT)
return true;
if (Keyboard.current.xKey.IsPressed()) rotation.x += rotationSpeed;
if (Keyboard.current.yKey.IsPressed()) rotation.y += rotationSpeed;
if (Keyboard.current.zKey.IsPressed()) rotation.z += rotationSpeed;
if (m_handType == VirtualGrasp.VG_HandSide.LEFT ? Mouse.current.leftButton.isPressed : Mouse.current.rightButton.isPressed)
mouse_held = Mathf.Min(filter, mouse_held + 1);
else mouse_held = Mathf.Max(0, mouse_held - 1);
depth = Mathf.Clamp(depth + Mouse.current.scroll.y.ReadValue() / 100.0f, .01f, 1.0f);
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
#else
if (Input.GetMouseButton(m_handType == VirtualGrasp.VG_HandSide.LEFT ? 0 : 1)) mouse_held = Mathf.Min(filter, mouse_held + 1);
else mouse_held = Mathf.Max(0, mouse_held - 1);
depth = Mathf.Clamp(depth + Input.mouseScrollDelta.y / 10.0f, .5f, 3.0f);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
#endif
Quaternion q = Camera.main.transform.rotation;
Vector3 p = ray.origin + depth * ray.direction + q * (m_handType == VirtualGrasp.VG_HandSide.LEFT ? Vector3.left : Vector3.right) * .1f;
q = q * Quaternion.Euler(rotation);
SetPose(0, Matrix4x4.TRS(p, q, Vector3.one));
return true;
}
public override float GetGrabStrength()
{
return (float)mouse_held / filter;
}
public override Color GetConfidence()
{
return Color.yellow;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
}
}
}

View File

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

View File

@@ -0,0 +1,115 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_OVRHAND_CONTROLLER // Please read below instructions and requirements before activating.
using UnityEngine;
namespace VirtualGrasp.Controllers
{
/**
* NOTE: this is an experimental controller only for the OculusIntegration sample.
* We recommend to not use OVRHand / OVRCustomSkeleton but use one of the
* various finger controllers that come with VirtualGrasp.
*
* This is an external controller class that supports a generic overlay controller for the OVRHand class as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_OVRHAND_CONTROLLER above and use the controller:
* - You have the Oculus SDK (https://www.oculus.com/setup/) installed on your computer.
* - You have the Oculus Integration plugin from https://developer.oculus.com/downloads/package/unity-integration/ imported into your Unity project.
* - You are using a handmodel / rig that is based on the OVRHand / OVRCustomSkeleton classes.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_ovr." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_OVR : VG_ExternalController
{
#if VG_USE_OVRHAND_CONTROLLER
private OVRCustomSkeleton m_skeleton = null;
private OVRHand m_hand = null;
#endif
public VG_EC_OVR(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_zeroOffsets = true; // the generic hand works on the Unity transforms, so it can't use offsets.
m_origin = origin;
m_enablingDefine = "VG_USE_OVRHAND_CONTROLLER";
#if VG_USE_OVRHAND_CONTROLLER
Initialize();
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public new void Initialize()
{
m_mapping = new VG_EC_Oculus.HandMapping();
#if VG_USE_OVRHAND_CONTROLLER
foreach (OVRCustomSkeleton hand in GameObject.FindObjectsOfType<OVRCustomSkeleton>()) {
if (hand.GetSkeletonType() == OVRSkeleton.SkeletonType.HandLeft && m_handType == VG_HandSide.LEFT ||
hand.GetSkeletonType() == OVRSkeleton.SkeletonType.HandRight && m_handType == VG_HandSide.RIGHT) {
m_skeleton = hand;
m_hand = hand.gameObject.GetComponent<OVRHand>();
}
}
if (m_skeleton == null)
{
m_initialized = false;
return;
}
base.Initialize();
m_initialized = true;
#else
m_initialized = false;
#endif
}
public override bool Compute()
{
if (!m_enabled) return false;
if (!m_initialized) { Initialize(); return false; }
#if VG_USE_OVRHAND_CONTROLLER
if (!m_skeleton.IsDataValid) return false;
int offset = (int)m_skeleton.GetCurrentStartBoneId();
for (int bone = 0; bone < m_mapping.GetNumBones(); bone++)
{
Transform pose = (bone != 1) ?
m_skeleton.CustomBones[offset + bone].transform :
m_skeleton.CustomBones[offset].transform; // we map forearm stub to wrist
SetPose(bone, Matrix4x4.TRS(pose.position, pose.rotation, Vector3.one));
}
return true;
#else
return false;
#endif
}
public override float GetGrabStrength() {
#if VG_USE_OVRHAND_CONTROLLER
return -1.0f;
//return m_hand.GetFingerPinchStrength(OVRHand.HandFinger.Index);
#else
return -1.0f;
#endif
}
public override Color GetConfidence()
{
return Color.yellow;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5) {
}
}
}

View File

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

View File

@@ -0,0 +1,200 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_OCULUS_CONTROLLER // Please read below instructions and requirements before activating.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the Oculus Finger Tracking controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_OCULUS_CONTROLLER above and use the controller:
* - You have the Oculus SDK (https://www.oculus.com/setup/) installed on your computer.
* - You have the Oculus Integration plugin from https://developer.oculus.com/downloads/package/unity-integration/ imported into your Unity project.
* - You have the same Oculus Integration plugin version as the one on your headset AND Oculus App.
* - You use Oculus through "Legacy OVR" (Oculus -> Tools -> OVR Utilitites Plugin -> Set OVR to Legacy LibOVR)
* - You have setup the AndroidManifest.xml properly, i.e. they need to include
* <uses-permission android:name="com.oculus.permission.HAND_TRACKING" />
* <uses-feature android:name="oculus.software.handtracking" android:required="false" />
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_oculus." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Oculus : VG_ExternalController
{
#if VG_USE_OCULUS_CONTROLLER
private OVRPlugin.Skeleton m_skeleton = new OVRPlugin.Skeleton();
private OVRPlugin.HandState m_currentState = new OVRPlugin.HandState();
#endif
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>() {
#if VG_USE_OCULUS_CONTROLLER
{ (int)OVRPlugin.BoneId.Hand_WristRoot, Hand_WristRoot },
{ (int)OVRPlugin.BoneId.Hand_ForearmStub, null }, // this is a child of wrist, but towards the arm
{ (int)OVRPlugin.BoneId.Hand_Thumb0, null },
{ (int)OVRPlugin.BoneId.Hand_Thumb1, Hand_Thumb1 },
{ (int)OVRPlugin.BoneId.Hand_Thumb2, Hand_Thumb2 },
{ (int)OVRPlugin.BoneId.Hand_Thumb3, Hand_Thumb3 },
{ (int)OVRPlugin.BoneId.Hand_Index1, Hand_Index1 },
{ (int)OVRPlugin.BoneId.Hand_Index2, Hand_Index2 },
{ (int)OVRPlugin.BoneId.Hand_Index3, Hand_Index3 },
{ (int)OVRPlugin.BoneId.Hand_Middle1, Hand_Middle1 },
{ (int)OVRPlugin.BoneId.Hand_Middle2, Hand_Middle2 },
{ (int)OVRPlugin.BoneId.Hand_Middle3, Hand_Middle3 },
{ (int)OVRPlugin.BoneId.Hand_Ring1, Hand_Ring1 },
{ (int)OVRPlugin.BoneId.Hand_Ring2, Hand_Ring2 },
{ (int)OVRPlugin.BoneId.Hand_Ring3, Hand_Ring3 },
{ (int)OVRPlugin.BoneId.Hand_Pinky0, null },
{ (int)OVRPlugin.BoneId.Hand_Pinky1, Hand_Pinky1 },
{ (int)OVRPlugin.BoneId.Hand_Pinky2, Hand_Pinky2 },
{ (int)OVRPlugin.BoneId.Hand_Pinky3, Hand_Pinky3 },
{ (int)OVRPlugin.BoneId.Hand_ThumbTip, null },
{ (int)OVRPlugin.BoneId.Hand_IndexTip, null },
{ (int)OVRPlugin.BoneId.Hand_MiddleTip, null },
{ (int)OVRPlugin.BoneId.Hand_RingTip, null },
{ (int)OVRPlugin.BoneId.Hand_PinkyTip, null }
#endif
};
m_BoneToParent = new Dictionary<int, int>();
#if VG_USE_OCULUS_CONTROLLER
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_ForearmStub] = (int)OVRPlugin.BoneId.Hand_WristRoot;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Thumb0] = (int)OVRPlugin.BoneId.Hand_WristRoot;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Thumb1] = (int)OVRPlugin.BoneId.Hand_Thumb0;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Thumb2] = (int)OVRPlugin.BoneId.Hand_Thumb1;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Thumb3] = (int)OVRPlugin.BoneId.Hand_Thumb2;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_ThumbTip] = (int)OVRPlugin.BoneId.Hand_Thumb3;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Index1] = (int)OVRPlugin.BoneId.Hand_WristRoot;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Index2] = (int)OVRPlugin.BoneId.Hand_Index1;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Index3] = (int)OVRPlugin.BoneId.Hand_Index2;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_IndexTip] = (int)OVRPlugin.BoneId.Hand_Index3;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Middle1] = (int)OVRPlugin.BoneId.Hand_WristRoot;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Middle2] = (int)OVRPlugin.BoneId.Hand_Middle1;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Middle3] = (int)OVRPlugin.BoneId.Hand_Middle2;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_MiddleTip] = (int)OVRPlugin.BoneId.Hand_Middle3;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Ring1] = (int)OVRPlugin.BoneId.Hand_WristRoot;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Ring2] = (int)OVRPlugin.BoneId.Hand_Ring1;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Ring3] = (int)OVRPlugin.BoneId.Hand_Ring2;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_RingTip] = (int)OVRPlugin.BoneId.Hand_Ring3;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Pinky0] = (int)OVRPlugin.BoneId.Hand_WristRoot;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Pinky1] = (int)OVRPlugin.BoneId.Hand_Pinky0;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Pinky2] = (int)OVRPlugin.BoneId.Hand_Pinky1;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_Pinky3] = (int)OVRPlugin.BoneId.Hand_Pinky2;
m_BoneToParent[(int)OVRPlugin.BoneId.Hand_PinkyTip] = (int)OVRPlugin.BoneId.Hand_Pinky3;
#endif
}
}
public VG_EC_Oculus(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enablingDefine = "VG_USE_OCULUS_CONTROLLER";
#if VG_USE_OCULUS_CONTROLLER
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public new void Initialize()
{
#if VG_USE_OCULUS_CONTROLLER
m_mapping = new HandMapping();
if (OVRPlugin.GetSkeleton(
(m_handType == VG_HandSide.LEFT) ? OVRPlugin.SkeletonType.HandLeft :
(m_handType == VG_HandSide.RIGHT) ? OVRPlugin.SkeletonType.HandRight : OVRPlugin.SkeletonType.None,
out m_skeleton))
{
base.Initialize();
m_initialized = true;
}
else m_initialized = false;
#endif
}
public override float GetGrabStrength()
{
#if VG_USE_OCULUS_CONTROLLER
if (m_initialized && m_currentState.Status.HasFlag(OVRPlugin.HandStatus.HandTracked))
return -1.0f; // let VG decide from full DOF
//return (m_currentState.PinchStrength[0] + m_currentState.PinchStrength[1] + m_currentState.PinchStrength[2]) / 3.0f;
#endif
return 0.0f;
}
private bool IsTracking()
{
#if VG_USE_OCULUS_CONTROLLER
return OVRPlugin.GetHandState(OVRPlugin.Step.Render, m_handType == VG_HandSide.LEFT ? OVRPlugin.Hand.HandLeft : OVRPlugin.Hand.HandRight, ref m_currentState)
&& m_currentState.Status.HasFlag(OVRPlugin.HandStatus.HandTracked);
#else
return false;
#endif
}
public override bool Compute()
{
if (!m_enabled) return false;
if (!m_initialized) { Initialize(); return false; }
#if VG_USE_OCULUS_CONTROLLER
if (!IsTracking()) return false;
for (int boneId = 0; boneId < GetNumBones(); ++boneId)
{
if (boneId == 0)
{
SetPose(boneId, Matrix4x4.TRS(
m_currentState.RootPose.Position.FromFlippedZVector3f(),
m_currentState.RootPose.Orientation.FromFlippedZQuatf(),
Vector3.one));
}
else SetPose(boneId, m_poses[m_mapping.GetParent(boneId)] *
Matrix4x4.TRS(
m_skeleton.Bones[boneId].Pose.Position.FromFlippedZVector3f(),
m_currentState.BoneRotations[boneId].FromFlippedZQuatf(),
Vector3.one));
}
return true;
#else
return false;
#endif
}
public override Color GetConfidence()
{
#if VG_USE_OCULUS_CONTROLLER
if (!m_initialized) return Color.black;
switch (m_currentState.HandConfidence)
{
case OVRPlugin.TrackingConfidence.High:
return Color.green;
case OVRPlugin.TrackingConfidence.Low:
return Color.red;
}
#endif
return Color.black;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
}
}
}

View File

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

View File

@@ -0,0 +1,90 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using UnityEngine;
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports a Script/GUI controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_script." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Script : VG_ExternalController
{
private Transform m_wrist = null;
private float m_grabStrength = 0.0f;
/**
* The wrist transform strength can be set and received from another Script/GUI.
* Changes in the wrist transform will affect the controller through Compute().
*/
public Transform WristTransform
{
get { return m_wrist; }
set { m_wrist = value; }
}
/**
* The grab strength can be set and received from another Script/GUI.
* Changes in the grab strength will affect the controller through GetGrabStrength().
*/
public float GrabStrength
{
get { return m_grabStrength; }
set { m_grabStrength = Mathf.Clamp01(value); }
}
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
{ 0, Hand_WristRoot }
};
}
}
public VG_EC_Script(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enabled = true;
Initialize();
}
public new void Initialize()
{
m_mapping = new HandMapping();
base.Initialize();
}
public override bool Compute()
{
if (!m_enabled || m_wrist == null) return false;
SetPose(0, Matrix4x4.TRS(m_wrist.position, m_wrist.rotation, Vector3.one));
return true;
}
public override float GetGrabStrength()
{
return m_grabStrength;
}
public override Color GetConfidence()
{
return Color.yellow;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
}
}
}

View File

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

View File

@@ -0,0 +1,214 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_STEAMVR_CONTROLLER
#if UNITY_ANDROID && !UNITY_EDITOR // SteamVR is not supported on Android
#undef VG_USE_STEAMVR_CONTROLLER
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
#if VG_USE_STEAMVR_CONTROLLER
using Valve.VR;
#endif
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the Steam Knuckles controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_STEAMVR_CONTROLLER above and use the controller:
* - You have the SteamVR Unity plugin from https://assetstore.unity.com/packages/tools/integration/steamvr-plugin-32647 imported into your Unity project.
* - You have Steam and the corresponding SteamVR SDK (https://store.steampowered.com/app/250820/SteamVR/) installed on your computer.
* - You have OpenVR Loader selected in Unity XR Management Project Settings.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_steam." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_Steam : VG_ExternalController
{
#if VG_USE_STEAMVR_CONTROLLER
private SteamVR_Action_Skeleton m_skeleton = null;
private bool m_skeletonActive = false;
#endif
[Serializable]
public class SteamHandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
#if VG_USE_STEAMVR_CONTROLLER
{ SteamVR_Skeleton_JointIndexes.root, null },
{ SteamVR_Skeleton_JointIndexes.wrist, Hand_WristRoot },
{ SteamVR_Skeleton_JointIndexes.thumbProximal, Hand_Thumb1 },
{ SteamVR_Skeleton_JointIndexes.thumbMiddle, Hand_Thumb2 },
{ SteamVR_Skeleton_JointIndexes.thumbDistal, Hand_Thumb3 },
{ SteamVR_Skeleton_JointIndexes.thumbTip, null },
{ SteamVR_Skeleton_JointIndexes.indexMetacarpal, null },
{ SteamVR_Skeleton_JointIndexes.indexProximal, Hand_Index1 },
{ SteamVR_Skeleton_JointIndexes.indexMiddle, Hand_Index2 },
{ SteamVR_Skeleton_JointIndexes.indexDistal, Hand_Index3 },
{ SteamVR_Skeleton_JointIndexes.indexTip, null },
{ SteamVR_Skeleton_JointIndexes.middleMetacarpal, null },
{ SteamVR_Skeleton_JointIndexes.middleProximal, Hand_Middle1 },
{ SteamVR_Skeleton_JointIndexes.middleMiddle, Hand_Middle2 },
{ SteamVR_Skeleton_JointIndexes.middleDistal, Hand_Middle3 },
{ SteamVR_Skeleton_JointIndexes.middleTip, null },
{ SteamVR_Skeleton_JointIndexes.ringMetacarpal, null },
{ SteamVR_Skeleton_JointIndexes.ringProximal, Hand_Ring1 },
{ SteamVR_Skeleton_JointIndexes.ringMiddle, Hand_Ring2 },
{ SteamVR_Skeleton_JointIndexes.ringDistal, Hand_Ring3 },
{ SteamVR_Skeleton_JointIndexes.ringTip, null },
{ SteamVR_Skeleton_JointIndexes.pinkyMetacarpal, null },
{ SteamVR_Skeleton_JointIndexes.pinkyProximal, Hand_Pinky1 },
{ SteamVR_Skeleton_JointIndexes.pinkyMiddle, Hand_Pinky2 },
{ SteamVR_Skeleton_JointIndexes.pinkyDistal, Hand_Pinky3 },
{ SteamVR_Skeleton_JointIndexes.pinkyTip, null }
#endif
};
m_BoneToParent = new Dictionary<int, int>()
{
};
#if VG_USE_STEAMVR_CONTROLLER
m_BoneToParent[SteamVR_Skeleton_JointIndexes.wrist] = SteamVR_Skeleton_JointIndexes.root;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.thumbProximal] = SteamVR_Skeleton_JointIndexes.wrist;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.thumbMiddle] = SteamVR_Skeleton_JointIndexes.thumbProximal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.thumbDistal] = SteamVR_Skeleton_JointIndexes.thumbMiddle;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.thumbTip] = SteamVR_Skeleton_JointIndexes.thumbDistal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.indexMetacarpal] = SteamVR_Skeleton_JointIndexes.wrist;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.indexProximal] = SteamVR_Skeleton_JointIndexes.indexMetacarpal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.indexMiddle] = SteamVR_Skeleton_JointIndexes.indexProximal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.indexDistal] = SteamVR_Skeleton_JointIndexes.indexMiddle;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.indexTip] = SteamVR_Skeleton_JointIndexes.indexDistal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.middleMetacarpal] = SteamVR_Skeleton_JointIndexes.wrist;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.middleProximal] = SteamVR_Skeleton_JointIndexes.middleMetacarpal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.middleMiddle] = SteamVR_Skeleton_JointIndexes.middleProximal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.middleDistal] = SteamVR_Skeleton_JointIndexes.middleMiddle;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.middleTip] = SteamVR_Skeleton_JointIndexes.middleDistal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.ringMetacarpal] = SteamVR_Skeleton_JointIndexes.wrist;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.ringProximal] = SteamVR_Skeleton_JointIndexes.ringMetacarpal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.ringMiddle] = SteamVR_Skeleton_JointIndexes.ringProximal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.ringDistal] = SteamVR_Skeleton_JointIndexes.ringMiddle;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.ringTip] = SteamVR_Skeleton_JointIndexes.ringDistal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.pinkyMetacarpal] = SteamVR_Skeleton_JointIndexes.wrist;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.pinkyProximal] = SteamVR_Skeleton_JointIndexes.pinkyMetacarpal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.pinkyMiddle] = SteamVR_Skeleton_JointIndexes.pinkyProximal;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.pinkyDistal] = SteamVR_Skeleton_JointIndexes.pinkyMiddle;
m_BoneToParent[SteamVR_Skeleton_JointIndexes.pinkyTip] = SteamVR_Skeleton_JointIndexes.pinkyDistal;
#endif
}
}
public VG_EC_Steam(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enablingDefine = "VG_USE_STEAMVR_CONTROLLER";
#if VG_USE_STEAMVR_CONTROLLER
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public new void Initialize()
{
#if VG_USE_STEAMVR_CONTROLLER
m_mapping = new SteamHandMapping();
base.Initialize();
SteamVR.Initialize();
m_skeleton = SteamVR_Input.GetAction<SteamVR_Action_Skeleton>(m_handType == VG_HandSide.LEFT ? "SkeletonLeftHand" : "SkeletonRightHand");
m_skeleton.SetRangeOfMotion(EVRSkeletalMotionRange.WithoutController);
//m_skeleton.SetSkeletalTransformSpace(Valve.VR.EVRSkeletalTransformSpace.Model);
m_initialized = true;
if (GetNumBones() == 0)
{
Debug.LogError("Could not find bone skeleton root (boneCount=" + GetNumBones() + "; skeleton active=" + m_skeletonActive + ").");
m_initialized = false;
}
#endif
}
public override float GetGrabStrength()
{
#if VG_USE_STEAMVR_CONTROLLER
if (m_skeleton == null) return 0.0f;
return (m_skeleton.fingerCurls[0] + m_skeleton.fingerCurls[1] + m_skeleton.fingerCurls[2]) / 3.0f;
#else
return 0.0f;
#endif
}
public override bool Compute()
{
#if VG_USE_STEAMVR_CONTROLLER
if (!m_enabled) return false;
if (!m_initialized) { Initialize(); return false; }
ETrackingResult res = m_skeleton.GetTrackingResult(m_handType == VG_HandSide.LEFT ? SteamVR_Input_Sources.LeftHand : SteamVR_Input_Sources.RightHand);
if (res == 0) return false;
m_skeletonActive = m_skeleton.GetActive();
for (int boneId = 0; boneId < (m_skeletonActive ? GetNumBones() : 2); ++boneId)
{
if (boneId == 0) // root
{
SetPose(boneId, Matrix4x4.identity);
}
else if (boneId == 1) // wrist
{
SetPose(boneId, Matrix4x4.TRS(m_skeleton.GetLocalPosition(), m_skeleton.GetLocalRotation(), Vector3.one));
}
else
{
SetPose(boneId, m_poses[m_mapping.GetParent(boneId)] * // When transform space is local / Parent
Matrix4x4.TRS(m_skeleton.bonePositions[boneId], m_skeleton.boneRotations[boneId], Vector3.one));
}
}
#endif
return true;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.01F, int finger = 5)
{
#if VG_USE_STEAMVR_CONTROLLER
SteamVR_Actions.default_Haptic[hand.m_side == VG_HandSide.LEFT ? SteamVR_Input_Sources.LeftHand : SteamVR_Input_Sources.RightHand].Execute(0, duration, 10, amplitude);
#endif
}
public override Color GetConfidence()
{
#if VG_USE_STEAMVR_CONTROLLER
EVRSkeletalTrackingLevel skeletalTrackingLevel = EVRSkeletalTrackingLevel.VRSkeletalTracking_Estimated;
if (m_skeleton == null || OpenVR.Input.GetSkeletalTrackingLevel(m_skeleton.handle, ref skeletalTrackingLevel) != EVRInputError.None)
return Color.black;
switch (skeletalTrackingLevel)
{
case EVRSkeletalTrackingLevel.VRSkeletalTracking_Full:
return Color.green;
case EVRSkeletalTrackingLevel.VRSkeletalTracking_Partial:
return Color.yellow;
case EVRSkeletalTrackingLevel.VRSkeletalTracking_Estimated:
return Color.red;
}
#endif
return Color.black;
}
}
}

View File

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

View File

@@ -0,0 +1,107 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_UNITYXR_CONTROLLER
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the UnityXR controller (such as provided by Pico or Oculus integrations) as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_UNITYXR_CONTROLLER above and use the controller:
* - You have the Unity XR Management package installed into your Unity project.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_unityxr." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_UnityXR : VG_ExternalController
{
private InputDevice m_device;
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
{ 0, Hand_WristRoot }
};
}
}
public VG_EC_UnityXR(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enabled = true;
Initialize();
}
public new void Initialize()
{
m_mapping = new HandMapping();
base.Initialize();
m_initialized = true;
}
public override bool Compute()
{
if (!m_enabled) return false;
if (!m_initialized) { Initialize(); return false; }
if (!m_device.isValid)
{
m_device = InputDevices.GetDeviceAtXRNode(m_handType == VG_HandSide.LEFT ? XRNode.LeftHand : XRNode.RightHand);
return false;
}
if (m_device.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 p) &&
m_device.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion q))
{
SetPose(0, Matrix4x4.TRS(p, q, Vector3.one));
return true;
}
return false;
}
public override float GetGrabStrength()
{
if (!m_initialized || !m_device.isValid) return 0.0f;
float trigger = 0.0f;
switch (VG_Controller.GetGraspButton())
{
case VG_VrButton.TRIGGER:
m_device.TryGetFeatureValue(CommonUsages.trigger, out trigger); break;
case VG_VrButton.GRIP:
m_device.TryGetFeatureValue(CommonUsages.grip, out trigger); break;
case VG_VrButton.GRIP_OR_TRIGGER:
m_device.TryGetFeatureValue(CommonUsages.trigger, out trigger);
m_device.TryGetFeatureValue(CommonUsages.grip, out float trigger2);
trigger = Mathf.Max(trigger, trigger2);
break;
}
return trigger;
}
public override Color GetConfidence()
{
return Color.yellow;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
if (!m_initialized || !m_device.isValid) return;
HapticCapabilities capabilities;
if (m_device.TryGetHapticCapabilities(out capabilities) && capabilities.supportsImpulse)
m_device.SendHapticImpulse(0, amplitude, duration);
}
}
}

View File

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

View File

@@ -0,0 +1,193 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_XRHANDS_CONTROLLER
using System;
using System.Collections.Generic;
using UnityEngine;
#if VG_USE_XRHANDS_CONTROLLER
using UnityEngine.XR.Hands;
using UnityEngine.XR.Management;
#endif
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the Unity XRHands controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG.
*
* The following requirements have to be met to be able to enable the #define VG_USE_XRHANDS_CONTROLLER above and use the controller:
* - You have followed the installation instructions of the Unity XRHands package,
* from https://docs.unity3d.com/Packages/com.unity.xr.hands@1.1/manual/
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_unityxrhands." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_UnityXRHands : VG_ExternalController
{
#if VG_USE_XRHANDS_CONTROLLER
XRHandSubsystem m_Subsystem = null;
XRHand m_hand;
XRHandSubsystem.UpdateSuccessFlags m_updateSuccessFlags = XRHandSubsystem.UpdateSuccessFlags.None;
#endif
[Serializable]
public class OpenXRHandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
#if VG_USE_XRHANDS_CONTROLLER
{ XRHandJointID.Wrist.ToIndex(), Hand_WristRoot },
{ XRHandJointID.Palm.ToIndex(), null },
{ XRHandJointID.ThumbMetacarpal.ToIndex(), Hand_Thumb1 },
{ XRHandJointID.ThumbProximal.ToIndex(), Hand_Thumb2 },
{ XRHandJointID.ThumbDistal.ToIndex(), Hand_Thumb3 },
{ XRHandJointID.ThumbTip.ToIndex(), null },
{ XRHandJointID.IndexMetacarpal.ToIndex(), null },
{ XRHandJointID.IndexProximal.ToIndex(), Hand_Index1 },
{ XRHandJointID.IndexIntermediate.ToIndex(), Hand_Index2 },
{ XRHandJointID.IndexDistal.ToIndex(), Hand_Index3 },
{ XRHandJointID.IndexTip.ToIndex(), null },
{ XRHandJointID.MiddleMetacarpal.ToIndex(), null },
{ XRHandJointID.MiddleProximal.ToIndex(), Hand_Middle1 },
{ XRHandJointID.MiddleIntermediate.ToIndex(), Hand_Middle2 },
{ XRHandJointID.MiddleDistal.ToIndex(), Hand_Middle3 },
{ XRHandJointID.MiddleTip.ToIndex(), null },
{ XRHandJointID.RingMetacarpal.ToIndex(), null },
{ XRHandJointID.RingProximal.ToIndex(), Hand_Ring1 },
{ XRHandJointID.RingIntermediate.ToIndex(), Hand_Ring2 },
{ XRHandJointID.RingDistal.ToIndex(), Hand_Ring3 },
{ XRHandJointID.RingTip.ToIndex(), null },
{ XRHandJointID.LittleMetacarpal.ToIndex(), null },
{ XRHandJointID.LittleProximal.ToIndex(), Hand_Pinky1 },
{ XRHandJointID.LittleIntermediate.ToIndex(), Hand_Pinky2 },
{ XRHandJointID.LittleDistal.ToIndex(), Hand_Pinky3 },
{ XRHandJointID.LittleTip.ToIndex(), null }
#endif
};
m_BoneToParent = new Dictionary<int, int>()
{
};
#if VG_USE_XRHANDS_CONTROLLER
m_BoneToParent[XRHandJointID.Palm.ToIndex()] = XRHandJointID.Wrist.ToIndex();
m_BoneToParent[XRHandJointID.ThumbMetacarpal.ToIndex()] = XRHandJointID.Wrist.ToIndex();
m_BoneToParent[XRHandJointID.ThumbProximal.ToIndex()] = XRHandJointID.ThumbMetacarpal.ToIndex();
m_BoneToParent[XRHandJointID.ThumbDistal.ToIndex()] = XRHandJointID.ThumbProximal.ToIndex();
m_BoneToParent[XRHandJointID.ThumbTip.ToIndex()] = XRHandJointID.ThumbDistal.ToIndex();
m_BoneToParent[XRHandJointID.IndexMetacarpal.ToIndex()] = XRHandJointID.Wrist.ToIndex();
m_BoneToParent[XRHandJointID.IndexProximal.ToIndex()] = XRHandJointID.IndexMetacarpal.ToIndex();
m_BoneToParent[XRHandJointID.IndexIntermediate.ToIndex()] = XRHandJointID.IndexProximal.ToIndex();
m_BoneToParent[XRHandJointID.IndexDistal.ToIndex()] = XRHandJointID.IndexIntermediate.ToIndex();
m_BoneToParent[XRHandJointID.IndexTip.ToIndex()] = XRHandJointID.IndexDistal.ToIndex();
m_BoneToParent[XRHandJointID.MiddleMetacarpal.ToIndex()] = XRHandJointID.Wrist.ToIndex();
m_BoneToParent[XRHandJointID.MiddleProximal.ToIndex()] = XRHandJointID.MiddleMetacarpal.ToIndex();
m_BoneToParent[XRHandJointID.MiddleIntermediate.ToIndex()] = XRHandJointID.MiddleProximal.ToIndex();
m_BoneToParent[XRHandJointID.MiddleDistal.ToIndex()] = XRHandJointID.MiddleIntermediate.ToIndex();
m_BoneToParent[XRHandJointID.MiddleTip.ToIndex()] = XRHandJointID.MiddleDistal.ToIndex();
m_BoneToParent[XRHandJointID.RingMetacarpal.ToIndex()] = XRHandJointID.Wrist.ToIndex();
m_BoneToParent[XRHandJointID.RingProximal.ToIndex()] = XRHandJointID.RingMetacarpal.ToIndex();
m_BoneToParent[XRHandJointID.RingIntermediate.ToIndex()] = XRHandJointID.RingProximal.ToIndex();
m_BoneToParent[XRHandJointID.RingDistal.ToIndex()] = XRHandJointID.RingIntermediate.ToIndex();
m_BoneToParent[XRHandJointID.RingTip.ToIndex()] = XRHandJointID.RingDistal.ToIndex();
m_BoneToParent[XRHandJointID.LittleMetacarpal.ToIndex()] = XRHandJointID.Wrist.ToIndex();
m_BoneToParent[XRHandJointID.LittleProximal.ToIndex()] = XRHandJointID.LittleMetacarpal.ToIndex();
m_BoneToParent[XRHandJointID.LittleIntermediate.ToIndex()] = XRHandJointID.LittleProximal.ToIndex();
m_BoneToParent[XRHandJointID.LittleDistal.ToIndex()] = XRHandJointID.LittleIntermediate.ToIndex();
m_BoneToParent[XRHandJointID.LittleTip.ToIndex()] = XRHandJointID.LittleDistal.ToIndex();
#endif
}
}
public VG_EC_UnityXRHands(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enablingDefine = "VG_USE_XRHANDS_CONTROLLER";
#if VG_USE_XRHANDS_CONTROLLER
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public new void Initialize()
{
#if VG_USE_XRHANDS_CONTROLLER
m_mapping = new OpenXRHandMapping();
base.Initialize();
m_Subsystem = XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem<XRHandSubsystem>();
if (m_Subsystem != null)
{
if (m_handType == VG_HandSide.LEFT)
{
m_hand = m_Subsystem.leftHand;
m_updateSuccessFlags = XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose | XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints;
}
else
{
m_hand = m_Subsystem.rightHand;
m_updateSuccessFlags = XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose | XRHandSubsystem.UpdateSuccessFlags.RightHandJoints;
}
if (!m_Subsystem.running) m_Subsystem.Start();
base.Initialize();
m_initialized = true;
}
else m_initialized = false;
#endif
}
public override float GetGrabStrength()
{
return -1.0f; // let VG decide from full DOF
}
public override bool Compute()
{
if (!m_enabled) return false;
if (!m_initialized) { Initialize(); return false; }
#if VG_USE_XRHANDS_CONTROLLER
if (!m_Subsystem.running) return false;
if (!m_Subsystem.TryUpdateHands(XRHandSubsystem.UpdateType.Dynamic).HasFlag(m_updateSuccessFlags))
return false;
for (int boneId = 0; boneId < GetNumBones(); ++boneId)
{
if (m_hand.GetJoint(XRHandJointIDUtility.FromIndex(boneId)).TryGetPose(out Pose pose))
{
//if (m_origin != null) pose = pose.GetTransformedBy(m_origin);
SetPose(boneId, Matrix4x4.TRS(
pose.position,
pose.rotation, Vector3.one));
}
}
return true;
#else
return false;
#endif
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.01F, int finger = 5)
{
}
public override Color GetConfidence()
{
if (!m_initialized) return Color.black;
return Color.yellow;
}
}
}

View File

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

View File

@@ -0,0 +1,201 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define VG_USE_UNITYXRINTERACTION_HAND
using System;
using System.Collections.Generic;
using UnityEngine;
#if VG_USE_UNITYXRINTERACTION_HAND
using UnityEngine.InputSystem;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Inputs;
#endif
namespace VirtualGrasp.Controllers
{
/**
* This is an external controller class that supports the action-based Unity XR Interaction toolkit controller as an external controller.
* Please refer to https://docs.virtualgrasp.com/controllers.html for the definition of an external controller for VG, and to
* https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.0/manual/index.html for the plugin itself.
*
* The following requirements have to be met to be able to enable the #define VG_USE_UNITYINTERACTION_HAND above and use the controller:
* - You have the "XR Plugin Management" package installed into your Unity project.
* - You have the "XR Interaction Toolkit" package installed into your Unity project.
* - The current setup does not seem to work for OpenXR as an XR plugin provider, you must have Oculus XR package installed into your Unity project.
* - if you use Oculus, you use it through "OpenXR" (Oculus -> Tools -> OVR Utilities Plugin -> Set OVR to OpenXR)
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_vg_ec_unityxrinteraction." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_EC_UnityXRInteraction : VG_ExternalController
{
#if VG_USE_UNITYXRINTERACTION_HAND
private const string XRI_RESOURCE = "VG_XRI_Entries"; //"XRI Default Input Actions";
private const string XRI_ACTIONMAP = "Player";
private static VG_XRI_Entries m_xriEntries = null;
static InputActionManager m_provider = null;
private ActionBasedController m_controller = null;
#endif
[Serializable]
public class HandMapping : VG_BoneMapping
{
public override void Initialize(int avatarID, VG_HandSide side)
{
base.Initialize(avatarID, side);
m_BoneToTransform = new Dictionary<int, Transform>()
{
{ 0, Hand_WristRoot }
};
}
}
public VG_EC_UnityXRInteraction(int avatarID, VG_HandSide side, Transform origin)
{
m_avatarID = avatarID;
m_handType = side;
m_origin = origin;
m_enabled = true;
m_enablingDefine = "VG_USE_UNITYXRINTERACTION_HAND";
#if VG_USE_UNITYXRINTERACTION_HAND
m_enabled = true;
#else
PrintNotEnabledError();
m_enabled = false;
#endif
}
public void DisposeController()
{
#if VG_USE_UNITYXRINTERACTION_HAND
if (m_controller != null)
GameObject.Destroy(m_controller.gameObject);
#endif
}
public new void Initialize()
{
#if VG_USE_UNITYXRINTERACTION_HAND
if (m_provider == null) m_provider = GameObject.FindObjectOfType<InputActionManager>();
if (m_xriEntries == null)
m_xriEntries = Resources.Load<VG_XRI_Entries>(XRI_RESOURCE);
if (m_xriEntries == null)
{
Debug.LogError("Could not find " + XRI_RESOURCE);
return;
}
else
{
m_xriEntries.Initialize();
//Debug.Log(m_xriEntries);
}
InputActionAsset inputActionAsset = null;
if (m_provider == null)
{
m_provider = GameObject.FindObjectOfType<VG_MainScript>().gameObject.AddComponent<InputActionManager>();
inputActionAsset = Resources.Load<InputActionAsset>(m_xriEntries.Get(VG_XRI_Entries.XRI_Entry.RESOURCE));
if (inputActionAsset != null)
{
m_provider.actionAssets = new List<InputActionAsset>{ inputActionAsset };
m_provider.EnableInput();
}
else Debug.Log("Could not load input actions.");
}
if (m_provider != null && m_provider.actionAssets.Count > 0 && m_provider.actionAssets[0] != null)
{
m_mapping = new HandMapping();
base.Initialize();
string handSide = (m_handType == VG_HandSide.LEFT) ? "Left" : "Right";
// We put the ActionBasedController components on dummy GameObjects so they do not
// affect the wrist transforms per se (this can't be disabled in the XRController it seems).
GameObject controller = new(handSide + "Controller_ID" + m_avatarID);
controller.transform.SetParent(m_provider.transform);
m_controller = controller.AddComponent<ActionBasedController>();
// Bind Position and Rotation Signals
InputActionMap inputMap = m_provider.actionAssets[0].FindActionMap(XRI_ACTIONMAP);
if (inputMap == null) Debug.LogError("Could not find map " + XRI_ACTIONMAP);
else
{
m_controller.enableInputTracking = true;
m_controller.updateTrackingType = XRBaseController.UpdateType.Update;
foreach (VG_XRI_Entries.XRI_Entry entry in new List<VG_XRI_Entries.XRI_Entry> { VG_XRI_Entries.XRI_Entry.POSITION, VG_XRI_Entries.XRI_Entry.ROTATION, VG_XRI_Entries.XRI_Entry.TRIGGER, VG_XRI_Entries.XRI_Entry.GRAB, VG_XRI_Entries.XRI_Entry.HAPTICS })
{
string actionName = m_xriEntries.Get(entry, handSide);
InputAction inputAction = inputMap.FindAction(actionName);
if (inputAction == null)
{
Debug.LogWarning("Could not find action " + actionName + " in " + XRI_ACTIONMAP + ".");
continue;
}
switch (entry)
{
case VG_XRI_Entries.XRI_Entry.POSITION:
m_controller.positionAction = new InputActionProperty(inputAction); break;
case VG_XRI_Entries.XRI_Entry.ROTATION:
m_controller.rotationAction = new InputActionProperty(inputAction); break;
case VG_XRI_Entries.XRI_Entry.TRIGGER:
m_controller.activateAction = new InputActionProperty(inputAction); break;
case VG_XRI_Entries.XRI_Entry.GRAB:
m_controller.selectAction = new InputActionProperty(inputAction); break;
case VG_XRI_Entries.XRI_Entry.HAPTICS:
m_controller.hapticDeviceAction = new InputActionProperty(inputAction); break;
}
}
}
}
m_initialized = (m_provider != null && m_controller != null);
#endif
}
public override bool Compute()
{
#if VG_USE_UNITYXRINTERACTION_HAND
if (!m_enabled) return false;
if (!m_initialized) { Initialize(); return false; }
SetPose(0, Matrix4x4.TRS(m_controller.currentControllerState.position, m_controller.currentControllerState.rotation, Vector3.one));
#endif
return true;
}
public override float GetGrabStrength()
{
float trigger = 0.0f;
#if VG_USE_UNITYXRINTERACTION_HAND
if (!m_initialized) return 0.0f;
switch (VG_Controller.GetGraspButton())
{
case VG_VrButton.TRIGGER:
trigger = m_controller.currentControllerState.activateInteractionState.value; break;
case VG_VrButton.GRIP:
trigger = m_controller.currentControllerState.selectInteractionState.value; break;
case VG_VrButton.GRIP_OR_TRIGGER:
trigger = Mathf.Max(m_controller.currentControllerState.activateInteractionState.value,
m_controller.currentControllerState.selectInteractionState.value);
break;
}
#endif
return trigger;
}
public override Color GetConfidence()
{
return Color.yellow;
}
public override void HapticPulse(VG_HandStatus hand, float amplitude = 0.5F, float duration = 0.015F, int finger = 5)
{
#if VG_USE_UNITYXRINTERACTION_HAND
m_controller.SendHapticImpulse(amplitude, duration);
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,63 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VirtualGrasp.Controllers
{
[Serializable, CreateAssetMenu(fileName = "VG_XRI_Entries", menuName = "VirtualGrasp/VG_XRI_Entries")]
public class VG_XRI_Entries : ScriptableObject
{
public enum XRI_Entry
{
RESOURCE,
POSITION,
ROTATION,
TRIGGER,
GRAB,
HAPTICS
}
public void Initialize()
{
m_entries.Clear();
TryAdd(XRI_Entry.RESOURCE, m_resourceName);
TryAdd(XRI_Entry.POSITION, m_positionAction);
TryAdd(XRI_Entry.ROTATION, m_rotationAction);
TryAdd(XRI_Entry.TRIGGER, m_triggerAction);
TryAdd(XRI_Entry.GRAB, m_grabAction);
TryAdd(XRI_Entry.HAPTICS, m_hapticsAction);
}
public void TryAdd(XRI_Entry key, string value)
{
if (value != "")
m_entries.Add(key, value);
}
public string Get(XRI_Entry key, string suffix = "")
{
if (!m_entries.TryGetValue(key, out string value))
return "null";
return value + suffix;
}
public override string ToString()
{
string str = "Valid Entries:\n";
foreach (var entry in m_entries) { str += entry.ToString() + "\n"; }
return str;
}
public string m_resourceName = "";
public string m_positionAction = "";
public string m_rotationAction = "";
public string m_triggerAction = "";
public string m_grabAction = "";
public string m_hapticsAction = "";
private Dictionary<XRI_Entry, string> m_entries = new();
}
}

View File

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

View File

@@ -0,0 +1,218 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine.Events;
namespace VirtualGrasp.Scripts
{
using static VG_FingerAnimator.VG_FingerAnimationData;
/**
* VG_FingerAnimator animates the fingers on a selected hand side using a list of animation clips.
* Useful for making manual corrections on primary grasps,
* or animating fingers during inhand manipulation of articulated objects.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vgfingeranimator." + VG_Version.__VG_VERSION__ + ".html")]
public partial class VG_FingerAnimator : MonoBehaviour
{
[SerializeField, Tooltip("Hand side to animate")]
private HandSide m_hand = HandSide.LEFT;
[SerializeField, Tooltip("Animation clips for directing finger animation")]
private List<VG_FingerAnimationData> m_fingerAnimations = new List<VG_FingerAnimationData>();
[SerializeField, Tooltip("Optional, only enable animation when this object is grasped. If unassigned this transform will be used.")]
private Transform m_interactableObject = null;
[SerializeField]
private AnimationEvents m_events = new AnimationEvents();
private Transform m_holdingHand = null;
private float m_animationDrive = 1.0f;
private Dictionary<Transform, Vector3> m_startRotation= new Dictionary<Transform, Vector3>();
private Dictionary<Transform, Vector3> m_targetRotation = new Dictionary<Transform, Vector3>();
/// <summary>
/// Pass in a float in range [0,1] to drive the animation
/// </summary>
public void DriveAnimation(float animationDrive)
{
this.m_animationDrive = Mathf.Clamp01(animationDrive);
}
/// <summary>
/// Stops animation drive, bones will be rotated according to values inside the animation clips
/// </summary>
public void StopAnimationDrive()
{
this.m_animationDrive = 1f;
}
void Awake()
{
if (m_interactableObject == null)
m_interactableObject = this.transform;
VG_Controller.OnObjectGrasped.AddListener(OnObjectGrasped);
VG_Controller.OnObjectReleased.AddListener(OnObjectReleased);
enabled = false;
}
private void OnObjectGrasped(VG_HandStatus status)
{
if (status.m_selectedObject != m_interactableObject) return;
m_holdingHand = status.m_hand;
this.enabled = true;
}
private void OnObjectReleased(VG_HandStatus status)
{
if (status.m_selectedObject != m_interactableObject) return;
if (m_holdingHand == status.m_hand)
{
m_holdingHand = null;
VG_Controller.GetGraspingAvatars(m_interactableObject, out var hands);
foreach (var hand in hands)
{
m_holdingHand = VG_Controller.GetHand(hand.Key, hand.Value).m_hand;
}
this.enabled = m_holdingHand != null;
}
}
void OnEnable()
{
m_events.OnAnimationStarted?.Invoke();
}
void OnDisable()
{
m_events.OnAnimationStopped?.Invoke();
}
private void LateUpdate()
{
for (int i = 0; i < m_fingerAnimations.Count; i++)
{
VG_FingerAnimationData animation = m_fingerAnimations[i];
if (animation.disabled) continue;
for (int fingerIndex = 0; fingerIndex < 5; fingerIndex++)
{
Finger fingerEnum = FingerEnumFromIndex(fingerIndex);
if (animation.finger.HasFlag(fingerEnum))
{
for (int boneIndex = 0; boneIndex < 3; boneIndex++)
{
Bone boneEnum = BoneEnumFromIndex(boneIndex);
if (animation.bone.HasFlag(boneEnum))
{
int avatarID = VG_Controller.GetHand(m_holdingHand).m_avatarID;
AnimateFingerBone(avatarID, fingerIndex, boneIndex, animation);
}
}
}
}
}
}
private void AnimateFingerBone(int avatarID, int fingerIndex, int boneIndex, VG_FingerAnimationData animation)
{
if (VG_Controller.GetFingerBone(avatarID, (VG_HandSide)this.m_hand, fingerIndex, boneIndex, out Transform bone) != VG_ReturnCode.SUCCESS)
return;
Vector3 startRotationEuler = bone.localRotation.eulerAngles;
Vector3 targetRotationEuler = startRotationEuler + animation.rotation;
if (m_startRotation.ContainsKey(bone))
startRotationEuler = m_startRotation[bone];
else
m_startRotation.Add(bone, startRotationEuler);
if (m_targetRotation.ContainsKey(bone))
targetRotationEuler = m_targetRotation[bone];
else
m_targetRotation.Add(bone, targetRotationEuler);
Quaternion startRotation = Quaternion.Euler(startRotationEuler);
Quaternion targetRotation = Quaternion.Euler(targetRotationEuler);
float targetAnimationDrive = this.m_animationDrive;
if (animation.ignoreDrive)
{
targetAnimationDrive = 1f;
}
else if (animation.invertDrive)
{
targetAnimationDrive = 1f - targetAnimationDrive;
}
bone.localRotation = Quaternion.Slerp(startRotation, targetRotation, targetAnimationDrive);
}
}
public partial class VG_FingerAnimator
{
public enum HandSide
{
LEFT = VG_HandSide.LEFT,
RIGHT = VG_HandSide.RIGHT
}
[Serializable]
public class VG_FingerAnimationData
{
[Tooltip("Disables this animation clip")]
public bool disabled = true;
[Tooltip("Makes this animation clip always reach target rotation, effectively ignoring any animation drive")]
public bool ignoreDrive = false;
[Tooltip("Makes this animation clip reach target rotation when the animation drive value is 0 rather than 1")]
public bool invertDrive = false;
[System.Flags]
public enum Finger
{
Thumb = 1, Index = 2, Middle = 4, Ring = 8, Pinky = 16
};
[Tooltip("Fingers to animate in this clip, multiple can be selected")]
public Finger finger = Finger.Thumb;
[System.Flags]
public enum Bone
{
Proximal = 1, Middle = 2, Distal = 4
};
[Tooltip("Bones to animate in this clip, multiple can be selected")]
public Bone bone = Bone.Proximal;
[Tooltip("Target rotation relative to the initial rotation for selected bones")]
public Vector3 rotation;
public static Bone BoneEnumFromIndex(int boneIndex)
{
return boneIndex switch
{
0 => Bone.Proximal,
1 => Bone.Middle,
2 => Bone.Distal,
_ => throw new ArgumentException($"Couldn't map index {boneIndex} to bone enum")
};
}
public static Finger FingerEnumFromIndex(int fingerIndex)
{
return fingerIndex switch
{
0 => Finger.Thumb,
1 => Finger.Index,
2 => Finger.Middle,
3 => Finger.Ring,
4 => Finger.Pinky,
_ => throw new ArgumentException($"Couldn't map index {fingerIndex} to finger enum")
};
}
}
[Serializable]
public class AnimationEvents
{
[SerializeField]
public UnityEvent OnAnimationStarted, OnAnimationStopped;
}
}
}

View File

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

View File

@@ -0,0 +1,296 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using UnityEngine.UI; // for Text UI
using System.Collections.Generic;
namespace VirtualGrasp.Scripts
{
/**
* VG_Highlighter exemplifies how you could enable runtime grasp editing into your application.
* The MonoBehavior provides a tutorial on the VG API functions for some of the VG_Controller event functions,
* such as EditGrasp, GetInteractionTypeForObject and SetInteractionTypeForObject.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vggraspannotator." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_GraspEditor : MonoBehaviour
{
/// An enum to describe a hand interaction type allowed for grasp editing (adding primary grasps)
public enum VG_EditingInteractionType
{
TRIGGER_GRASP = 0, // Default, hand goes to object at grasp position
PREVIEW_GRASP = 1, // Grasp is always previewed once object is selected, trigger will pick up the object
JUMP_GRASP = 3, // Object jumps to hand when grasp is triggered
}
public Transform m_pad = null;
public Transform m_addGraspButton = null;
public Transform m_toggleInteractionButton = null;
public Transform m_stepGraspButton = null;
public Transform m_deleteGraspButton = null;
public Transform m_deleteAllGraspsButton = null;
[Tooltip("Interaction type used to add grasps.")]
public VG_EditingInteractionType m_editingInteractionType = VG_EditingInteractionType.TRIGGER_GRASP;
private bool m_sensorAvatarFound = false;
private int m_sensorAvatarIDLeft = 0;
private int m_sensorAvatarIDRight = 0;
private List<ButtonContainer> m_containers = new List<ButtonContainer>();
// Keep track of intersections
private Dictionary<Transform, bool> m_intersections = new Dictionary<Transform, bool>();
private delegate void EditFunction(VG_HandStatus hand);
private delegate bool ValidateFunction(VG_HandStatus hand, out string text);
private class ButtonContainer
{
public Transform m_root = null;
public EditFunction m_editFunction = null;
public ValidateFunction m_validateFunction = null;
private Text m_text = null;
private MeshRenderer m_renderer = null;
private VG_Articulation m_articulation = null;
public static HashSet<Transform> BUTTON_TRANSFORMS = new HashSet<Transform>();
public ButtonContainer(Transform button, ValidateFunction validateFunc, EditFunction editFunc)
{
m_root = button;
m_text = button.GetComponentInChildren<Text>();
m_renderer = button.GetComponentInChildren<MeshRenderer>();
m_articulation = button.GetComponentInChildren<VG_Articulation>();
BUTTON_TRANSFORMS.Add(m_articulation.transform);
m_editFunction = editFunc;
m_validateFunction = validateFunc;
}
public bool Validate(VG_HandStatus hand)
{
bool valid = m_validateFunction(hand, out string text);
if (m_text != null)
{
m_text.text = text;
m_text.color = (valid && hand.IsHolding()) ? Color.black : Color.grey;
}
return valid;
}
public bool Trigger(VG_HandStatus hand, bool hasIntersection)
{
if (m_renderer == null || !hand.IsHolding())
return hasIntersection;
VG_Controller.GetObjectJointState(m_root, out float state);
float threshold = m_articulation.m_min + (m_articulation.m_max - m_articulation.m_min) * 0.2f;
bool isIntersecting = state > threshold;
if (hasIntersection != isIntersecting)
{
VG_Controller.OnObjectCollided.Invoke(hand); // just to trigger haptics
if (isIntersecting) m_editFunction(hand);
}
return isIntersecting;
}
}
private void Start()
{
m_containers.Add(new ButtonContainer(m_addGraspButton, ValidateAddGrasp, AddGrasp));
m_containers.Add(new ButtonContainer(m_toggleInteractionButton, ValidateToggleInteraction, ToggleInteraction));
m_containers.Add(new ButtonContainer(m_stepGraspButton, ValidateStepGrasp, StepGrasp));
m_containers.Add(new ButtonContainer(m_deleteGraspButton, ValidateDeleteGrasp, DeleteGrasp));
m_containers.Add(new ButtonContainer(m_deleteAllGraspsButton, ValidateDeleteAllGrasp, DeleteAllGrasp));
if (VG_Controller.GetSensorControlledAvatarID(out m_sensorAvatarIDLeft, out m_sensorAvatarIDRight) == VG_ReturnCode.SUCCESS)
m_sensorAvatarFound = true;
}
#region ContainerFunctions
private bool IsValidObject(VG_HandStatus hand)
{
return hand != null && hand.m_selectedObject != null && hand.m_selectedObject.TryGetComponent<MeshRenderer>(out _);
}
private void AddGrasp(VG_HandStatus hand)
{
VG_Controller.EditGrasp(hand.m_avatarID, hand.m_side, VG_EditorAction.ADD_CURRENT);
}
private bool ValidateAddGrasp(VG_HandStatus hand, out string text)
{
if (!IsValidObject(hand))
{
text = "Add Grasp";
return false;
}
if (VG_Controller.GetInteractionTypeForObject(hand.m_selectedObject) == VG_InteractionType.JUMP_PRIMARY_GRASP)
{
text = "No add grasp\ninteraction is JUMP_PRIMARY_GRASP!";
return false;
}
text = "Add Grasp\n(" + hand.GetNumGraspsInDB() + ")";
return true;
}
private void ToggleInteraction(VG_HandStatus hand)
{
VG_InteractionType current_type = VG_Controller.GetInteractionTypeForObject(hand.m_selectedObject);
// Initially if object has non ideal grasp editing interaction type, first toggle allow switch to ideal editing interaction type
if (current_type != (VG_InteractionType)m_editingInteractionType && current_type != VG_InteractionType.JUMP_PRIMARY_GRASP)
VG_Controller.SetInteractionTypeForObject(hand.m_selectedObject, (VG_InteractionType)m_editingInteractionType);
// Then later will always toggle between the ideal editing type and jump primary grasp type
else
VG_Controller.SetInteractionTypeForObject(hand.m_selectedObject, current_type != VG_InteractionType.JUMP_PRIMARY_GRASP ?
VG_InteractionType.JUMP_PRIMARY_GRASP : (VG_InteractionType)m_editingInteractionType);
}
private void StepGrasp(VG_HandStatus hand)
{
VG_Controller.TogglePrimaryGraspOnObject(hand.m_avatarID, hand.m_side, hand.m_selectedObject);
}
private bool ValidateToggleInteraction(VG_HandStatus hand, out string text)
{
if (!IsValidObject(hand))
{
text = "Toggle interaction";
return false;
}
VG_InteractionType current_type = VG_Controller.GetInteractionTypeForObject(hand.m_selectedObject);
if (current_type == (VG_InteractionType)m_editingInteractionType && hand.GetNumPrimaryGraspsInDB() == 0)
{
text = "No toggle interaction\nno grasp!";
return false;
}
VG_InteractionType target_type = (current_type == (VG_InteractionType)m_editingInteractionType) ? VG_InteractionType.JUMP_PRIMARY_GRASP : (VG_InteractionType)m_editingInteractionType;
text = "Toggle interaction To\n" + target_type + "";
return true;
}
private bool ValidateStepGrasp(VG_HandStatus hand, out string text)
{
if (!IsValidObject(hand))
{
text = "Step grasp";
return false;
}
if (hand.GetNumGraspsInDB() == 0)
{
text = "No step grasp\nno grasp!";
return false;
}
VG_InteractionType currentInteractionType = VG_Controller.GetInteractionTypeForObject(hand.m_selectedObject);
if (currentInteractionType != VG_InteractionType.JUMP_PRIMARY_GRASP)
{
text = "No step grasp\ninteraction is not JUMP_PRIMARY_GRASP!";
return false;
}
text = "Step grasp";
return true;
}
private bool ValidateDeleteGrasp(VG_HandStatus hand, out string text)
{
if (!IsValidObject(hand))
{
text = "Delete grasp";
return false;
}
int numGrasps = hand.GetNumGraspsInDB();
if (numGrasps == 0)
{
text = "No delete grasp";
return false;
}
text = "Delete grasp\n(" + numGrasps + ")";
return true;
}
private bool ValidateDeleteAllGrasp(VG_HandStatus hand, out string text)
{
if (!IsValidObject(hand))
{
text = "Delete all grasps";
return false;
}
int numGrasps = hand.GetNumGraspsInDB();
if (numGrasps == 0)
{
text = "No delete all grasps";
return false;
}
text = "Delete all grasps\n(" + numGrasps + ")";
return true;
}
private void DeleteGrasp(VG_HandStatus hand)
{
VG_Controller.EditGrasp(hand.m_avatarID, hand.m_side, VG_EditorAction.DELETE_CURRENT);
if (hand.GetNumGraspsInDB() == 0)
VG_Controller.SetInteractionTypeForObject(hand.m_selectedObject, (VG_InteractionType)m_editingInteractionType);
}
private void DeleteAllGrasp(VG_HandStatus hand)
{
VG_Controller.EditGrasp(hand.m_avatarID, hand.m_side, VG_EditorAction.DELETE_ALL_HAND_GRASPS);
if (VG_Controller.GetNumGraspsInDB(hand.m_selectedObject, hand.m_avatarID, hand.m_side) == 0)
VG_Controller.SetInteractionTypeForObject(hand.m_selectedObject, (VG_InteractionType)m_editingInteractionType);
else
Debug.LogError("After remove all hand grasp, num grasps still non zero");
}
#endregion // ContainerFunctions
void Update()
{
if (!m_sensorAvatarFound)
{
if (VG_Controller.GetSensorControlledAvatarID(out m_sensorAvatarIDLeft, out m_sensorAvatarIDRight) == VG_ReturnCode.SUCCESS)
m_sensorAvatarFound = true;
}
if (!m_sensorAvatarFound)
return;
// Find selected object for left and right hand, but ignore if selected object is the pad or a button for this annotator
Transform leftSelected = null;
Transform rightSelected = null;
VG_HandStatus status = VG_Controller.GetHand(m_sensorAvatarIDLeft, VG_HandSide.LEFT);
if (status != null)
leftSelected = ButtonContainer.BUTTON_TRANSFORMS.Contains(status.m_selectedObject) || status.m_selectedObject == m_pad ? null : status.m_selectedObject;
status = VG_Controller.GetHand(m_sensorAvatarIDRight, VG_HandSide.RIGHT);
if (status != null)
rightSelected = ButtonContainer.BUTTON_TRANSFORMS.Contains(status.m_selectedObject) || status.m_selectedObject == m_pad ? null : status.m_selectedObject;
// If no object selected or if both hand selected different objects, not allow annotating
if ((leftSelected == null && rightSelected == null) ||
(leftSelected != null && rightSelected != null && leftSelected != rightSelected))
{
foreach (ButtonContainer container in m_containers)
if (!container.Validate(null)) continue;
return;
}
bool hasIntersection;
VG_HandStatus hand = VG_Controller.GetHand((leftSelected != null) ? m_sensorAvatarIDLeft : m_sensorAvatarIDRight,
(leftSelected != null) ? VG_HandSide.LEFT : VG_HandSide.RIGHT);
foreach (ButtonContainer container in m_containers)
{
if (!container.Validate(hand)) continue;
// cache intersecting value in map so function only triggers in entering/exiting frame
hasIntersection = (m_intersections.ContainsKey(container.m_root)) ? m_intersections[container.m_root] : false;
m_intersections[container.m_root] = container.Trigger(hand, hasIntersection);
}
}
}
}

View File

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

View File

@@ -0,0 +1,33 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using System.Collections.Generic;
namespace VirtualGrasp.Scripts
{
/**
* VG_HandStatus_Debugger provides a tool to show the VG_HandStatus members during runtime in editor mode.
* The MonoBehavior provides a tutorial on the VG API functions for using the VG_HandStatus.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vghandstatusdebugger." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_HandStatusDebugger : MonoBehaviour
{
[Tooltip("This list will be updated during runtime with the VG_HandStatus of all hands.")]
public List<VG_HandStatus> m_hands = new List<VG_HandStatus>();
#if UNITY_EDITOR
public void Start()
{
this.hideFlags = HideFlags.NotEditable;
}
public void Update()
{
m_hands.Clear();
foreach (VG_HandStatus hand in VG_Controller.GetHands())
m_hands.Add(hand);
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,79 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
namespace VirtualGrasp.Scripts
{
/**
* VG_HandVisualizer provides a tool to visualize the hand bones in Unity.
* The MonoBehavior provides a tutorial on the VG API functions for accessing specific bones / elements of the hands.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vghandvisualizer." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_HandVisualizer : MonoBehaviour
{
private Dictionary<int, GameObject> m_limbs = new();
private Dictionary<int, LineRenderer> m_lines = new();
private Transform m_root = null;
private void OnEnable()
{
m_root = new GameObject("DebugVis").transform;
VG_Controller.OnPostUpdate.AddListener(Visualize);
}
private void OnDisable()
{
VG_Controller.OnPostUpdate.RemoveListener(Visualize);
if (m_root != null)
DestroyImmediate(m_root.gameObject);
m_limbs.Clear();
m_lines.Clear();
}
void Visualize()
{
foreach (VG_HandStatus hand in VG_Controller.GetHands())
{
if (VG_Controller.GetBone(hand.m_avatarID, hand.m_side, VG_BoneType.WRIST, out int wrist_iid, out Vector3 pw, out Quaternion qw) == VG_ReturnCode.SUCCESS)
{
if (!m_limbs.ContainsKey(wrist_iid))
{
m_limbs[wrist_iid] = new GameObject("Avatar" + hand.m_avatarID + "_" + hand.m_side + "_wrist");
m_limbs[wrist_iid].transform.SetParent(m_root, true);
Destroy(m_limbs[wrist_iid].GetComponent<Collider>());
}
m_limbs[wrist_iid].transform.SetPositionAndRotation(pw, qw);
}
else continue;
foreach (int fingerId in new List<int>() { 0, 1, 2, 3, 4 })
{
foreach (int boneId in new List<int>() { 0, 1, 2, -1 })
{
if (VG_Controller.GetFingerBone(hand.m_avatarID, hand.m_side, fingerId, boneId, out int iid, out Vector3 pf, out Quaternion qf) == VG_ReturnCode.SUCCESS)
{
if (!m_limbs.ContainsKey(iid))
{
Transform last_bone = m_limbs.Last().Value.transform;
m_limbs[iid] = new GameObject(hand.m_side + "_" + VG_Controller.GetBone(iid).name + "_" + fingerId.ToString() + boneId.ToString());
m_limbs[iid].transform.SetParent(boneId == 0 ? m_limbs[wrist_iid].transform : last_bone, true);
Destroy(m_limbs[iid].GetComponent<Collider>());
m_lines[iid] = m_limbs[iid].AddComponent<LineRenderer>();
m_lines[iid].widthMultiplier = 0.002f;
m_lines[iid].positionCount = 2;
m_lines[iid].useWorldSpace = true;
}
m_limbs[iid].transform.SetPositionAndRotation(pf, qf);
m_lines[iid].SetPosition(0, m_limbs[iid].transform.parent.position);
m_lines[iid].SetPosition(1, pf);
}
}
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,278 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
//#define HIGHLIGHT_PLUS
//#define USE_CAKESLICE_OUTLINE // https://github.com/cakeslice/Outline-Effect
using UnityEngine;
using System.Collections.Generic;
#if HIGHLIGHT_PLUS
using HighlightPlus;
#endif
#if USE_CAKESLICE_OUTLINE
using cakeslice;
#endif
namespace VirtualGrasp.Scripts
{
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(VG_Highlighter))]
class VG_Highlighter_GUI : Editor
{
private class State
{
public bool enabled = true;
public string text = "";
public string tooltip = "";
public State(string _text, string _tooltip, bool _enabled = true)
{
text = _text;
tooltip = _tooltip;
enabled = _enabled;
}
}
private Dictionary<VG_ReturnCode, State> m_states = new Dictionary<VG_ReturnCode, State>() {
{ VG_ReturnCode.OBJECT_NO_BAKE, new State("No bakes.", "These objects have not been baked.") },
{ VG_ReturnCode.OBJECT_NO_GRASPS, new State("Dynamic Grasps", "These objects have been baked and will be available for dynamic grasping.") },
{ VG_ReturnCode.SUCCESS, new State("+ Static Grasps", "These objects have been baked and grasps have been added in GraspStudio.") }
};
private List<VG_ReturnCode> state_copy = new List<VG_ReturnCode>();
private HashSet<VG_ReturnCode> state_selected = new HashSet<VG_ReturnCode>();
public void OnEnable()
{
foreach (var state in m_states)
{
state_copy.Add(state.Key);
if (state.Value.enabled) state_selected.Add(state.Key);
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (!VG_Controller.IsEnabled())
{
GUILayout.Label(
"Play scene to highlight the state of objects in the scene.",
EditorStyles.wordWrappedLabel);
return;
}
GUILayout.Label(
"Select to highlight the state of objects in the scene.",
EditorStyles.wordWrappedLabel);
Color color;
bool state_changed = false;
GUIStyle myStyle = new GUIStyle(GUI.skin.button);
VG_Highlighter highlighter = target as VG_Highlighter;
EditorGUILayout.BeginHorizontal();
foreach (VG_ReturnCode code in state_copy)
{
color = highlighter.GetColor(code);
myStyle.normal.textColor = color;
myStyle.onNormal.textColor = color;
myStyle.onHover.textColor = color;
m_states[code].enabled = GUILayout.Toggle(m_states[code].enabled, new GUIContent(m_states[code].text, m_states[code].tooltip), myStyle);
bool this_enabled = !m_states[code].enabled;
if (this_enabled ^ state_selected.Contains(code)) state_changed |= true;
if (this_enabled) state_selected.Add(code);
else state_selected.Remove(code);
}
EditorGUILayout.EndHorizontal();
if (state_changed) highlighter.HighlightObjectStatus(state_selected);
}
}
#endif
/**
* VG_Highlighter exemplifies how you could enable object highlighting based on the current hand status.
* The MonoBehavior provides a tutorial on the VG API functions for some of the VG_Controller event functions, such as OnObjectSelected and OnObjectDeselected.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vghighlighter." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_Highlighter : MonoBehaviour
{
[Tooltip("Set the shader that is used for highlighting.")]
public Shader m_shader = null;
#if !HIGHLIGHT_PLUS && !USE_CAKESLICE_OUTLINE
private List<Material>[] m_unhighlightedMaterials = new List<Material>[2];
private List<Material>[] m_highlightedMaterials = new List<Material>[2];
#endif
[Tooltip("Set the color that are used for highlighting objects selected by the left hand.")]
public Color m_leftHandColor = Color.green;
[Tooltip("Set the color that are used for highlighting objects selected by the right hand.")]
public Color m_rightHandColor = Color.green;
// Dictionary to keep track of highlighted objects.
private Dictionary<VG_HandSide, Transform> m_highlightedObjects = new Dictionary<VG_HandSide, Transform>();
#if USE_CAKESLICE_OUTLINE
private Outline[] m_outlines = new Outline[2] { new Outline(), new Outline() };
#endif
#if HIGHLIGHT_PLUS
#endif
public Color GetColor(VG_ReturnCode code)
{
switch (code)
{
case VG_ReturnCode.OBJECT_NO_BAKE: return Color.red;
case VG_ReturnCode.OBJECT_NO_GRASPS: return Color.green;
case VG_ReturnCode.SUCCESS: return Color.cyan;
default: return Color.black;
}
}
void Start()
{
m_shader = Resources.Load<Shader>("SH_RimLight");
if (m_shader == null) Debug.LogWarning("No shader found/assigned to VG_Highlighter. Please assign a shader.", gameObject);
// Initialize the highlighted objects dictionary.
m_highlightedObjects[VG_HandSide.LEFT] = null;
m_highlightedObjects[VG_HandSide.RIGHT] = null;
m_leftHandColor.a = 0.5f;
m_rightHandColor.a = 0.5f;
VG_Controller.OnObjectSelected.AddListener(Highlight);
VG_Controller.OnObjectDeselected.AddListener(Unhighlight);
VG_Controller.OnObjectGrasped.AddListener(Unhighlight);
}
public void HighlightObjectStatus(HashSet<VG_ReturnCode> states)
{
bool isSelected;
int numSelected = 0;
int numAll = 0;
Color color;
foreach (Transform t in VG_Controller.GetSelectableObjects())
{
color = Color.black;
isSelected = false;
foreach (VG_ReturnCode state in states)
{
isSelected = VG_Controller.GetUnbakedObjects(state).Contains(t);
if (state == VG_ReturnCode.SUCCESS) isSelected = !isSelected;
if (isSelected) { color = GetColor(state); break; }
}
color.a = 0.5f;
if (t != null && t.GetComponentInChildren<MeshRenderer>() != null)
{
foreach (Material m in t.GetComponentInChildren<MeshRenderer>().materials)
{
m.shader = isSelected ? m_shader : Shader.Find("Legacy Shaders/Specular");
m.SetFloat("_RimPower", 0.25f);
m.SetColor("_RimColor", color);
}
}
if (isSelected) numSelected++;
numAll++;
}
if (states.Count > 0)
Debug.Log("Highlighting " + numSelected + " out of " + numAll + " interactable objects.");
}
private void Highlight(VG_HandStatus hand)
{
//if (VG_Controller.IsPushable(obj) &&
// !VG_Controller.IsGraspable(obj))
//return;
// If the selected object is already highlighted
if (hand.m_selectedObject == m_highlightedObjects[hand.m_side])
return;
// If the selected object is the same
if (hand.m_selectedObject == m_highlightedObjects[hand.m_side == VG_HandSide.LEFT ? VG_HandSide.RIGHT : VG_HandSide.LEFT])
return;
m_highlightedObjects[hand.m_side] = hand.m_selectedObject;
int id = hand.m_side < 0 ? 0 : 1;
Color color = id == 0 ? m_leftHandColor : m_rightHandColor;
#if HIGHLIGHT_PLUS
HighlightEffect highlight = hand.m_selectedObject.GetComponent<HighlightEffect>();
if (highlight == null)
{
highlight = hand.m_selectedObject.gameObject.AddComponent<HighlightPlus.HighlightEffect>();
highlight.highlighted = true;
highlight.glow = 1.0f;
highlight.overlayAnimationSpeed = 0.0f;
highlight.overlay = 0.0f;
highlight.seeThrough = SeeThroughMode.Never;
highlight.outlineVisibility = Visibility.AlwaysOnTop;
}
else highlight.highlighted = true;
for (int gp = 0; gp < highlight.glowPasses.Length; gp++)
highlight.glowPasses[gp].color = color;
highlight.outlineColor = color;
#else
#if USE_CAKESLICE_OUTLINE
m_outlines[id].Renderer = hand.m_selectedObject.GetComponent<Renderer>();
m_outlines[id].Enable();
#else
Material[] objectMaterials = hand.m_selectedObject.GetComponentInChildren<MeshRenderer>().sharedMaterials;
m_unhighlightedMaterials[id] = new List<Material>(hand.m_selectedObject.GetComponentInChildren<MeshRenderer>().sharedMaterials);
for (int i = 0, count = m_unhighlightedMaterials[id].Count; i < count; i++)
m_unhighlightedMaterials[id][i] = new Material(m_unhighlightedMaterials[id][i]);
m_highlightedMaterials[id] = new List<Material>();
for (int i = 0, count = m_unhighlightedMaterials[id].Count; i < count; i++)
{
m_highlightedMaterials[id].Add(new Material(m_unhighlightedMaterials[id][i]));
m_highlightedMaterials[id][i].shader = m_shader;
}
MeshRenderer objectRenderer = m_highlightedObjects[hand.m_side].GetComponentInChildren<MeshRenderer>();
for (int i = 0, count = objectRenderer.materials.Length; i < count; i++)
{
objectMaterials[i] = m_highlightedMaterials[id][i];
objectMaterials[i].SetColor("_RimColor", color);
}
objectRenderer.sharedMaterials = objectMaterials;
#endif
#endif
}
private void Unhighlight(VG_HandStatus hand)
{
int id = hand.m_side < 0 ? 0 : 1;
if (!m_highlightedObjects.ContainsKey(hand.m_side))
return;
// Got no object (or the same object as before), got no unhighlight
Transform highlightedObject = m_highlightedObjects[hand.m_side];
if (highlightedObject == null) return;
#if HIGHLIGHT_PLUS
if (hand.m_formerSelectedObject.GetComponent<HighlightPlus.HighlightEffect>() != null)
hand.m_formerSelectedObject.GetComponent<HighlightPlus.HighlightEffect>().highlighted = false;
#else
#if USE_CAKESLICE_OUTLINE
m_outlines[id].Disable();
m_outlines[id].Renderer = null;
#else
highlightedObject.GetComponentInChildren<MeshRenderer>().sharedMaterials = m_unhighlightedMaterials[id].ToArray();
#endif
#endif
m_highlightedObjects[hand.m_side] = null;
}
}
}

View File

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

View File

@@ -0,0 +1,115 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
namespace VirtualGrasp.Scripts
{
/**
* VG_HintVisualizer provides a tool to visualize some hints such as a selection sphere to debug object selection or a push sphere to guide pushing interactions.
* The MonoBehavior provides a tutorial on the VG API functions for accessing the push state (GetPushCircle).
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vghintvisualizer." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_HintVisualizer : MonoBehaviour
{
[Tooltip("Enable push hint visualization. For each hand, a circular hint will appear when the hand is in PUSHING mode.")]
public bool m_enablePushHints = true;
[Tooltip("Enable grasp hint visualization. For each hand, a sphere will appear as a hint of the current selection state.")]
public bool m_enableGraspHints = true;
[Tooltip("A push hint visualization (e.g. put a sphere here) when push mode is PUSHING.")]
public List<Transform> pushHints = new List<Transform> { };
[Tooltip("A selection hint visualization (e.g. put a sphere here) where the grasp selector is placed.")]
public List<Transform> graspHints = new List<Transform> { };
/// Add a collider-less sphere as a marker
private void AddHintObject(List<Transform> hintList, string name)
{
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
DestroyImmediate(go.GetComponent<Collider>());
go.name = name;
go.transform.localScale = Vector3.zero;
go.transform.SetParent(transform);
hintList.Add(go.transform);
}
private void RemoveHintObjects()
{
foreach (var hint in pushHints)
DestroyImmediate(hint.gameObject);
foreach (var hint in graspHints)
DestroyImmediate(hint.gameObject);
pushHints.Clear();
graspHints.Clear();
}
private void OnEnable()
{
VG_Controller.OnPostUpdate.AddListener(HintUpdate);
if (pushHints.Count == 0) AddHintObject(pushHints, "PushHint");
if (graspHints.Count == 0) AddHintObject(graspHints, "SelectionHint");
}
private void OnDisable()
{
VG_Controller.OnPostUpdate.RemoveListener(HintUpdate);
RemoveHintObjects();
}
void HintUpdate()
{
if (m_enablePushHints)
{
// Fill up the push hints assuming each avatar has 2 hands
while (pushHints.Count < VG_Controller.GetHands().Count())
{
pushHints.Add(GameObject.Instantiate(pushHints.Last()));
pushHints.Last().SetParent(transform);
}
int num = 0;
foreach (VG_HandStatus hand in VG_Controller.GetHands())
{
Transform t = VG_Controller.GetPushCircle(hand.m_avatarID, hand.m_side, out Vector3 p, out Quaternion q, out float radius, out bool inContact);
Transform hint = pushHints[num];
hint.gameObject.SetActive(t != null);
if (t != null)
{
hint.SetPositionAndRotation(p + q * new Vector3(0, 0, -0.001f), q);
hint.localScale = new Vector3(2 * radius, 2 * radius, 0.001f);
if (inContact) VG_Controller.OnObjectCollided.Invoke(hand);
}
num++;
}
}
if (m_enableGraspHints)
{
// Fill up the push hints assuming each avatar has 2 hands
while (graspHints.Count < VG_Controller.GetHands().Count())
{
graspHints.Add(GameObject.Instantiate(graspHints.Last()));
graspHints.Last().SetParent(transform);
}
int num = 0;
foreach (VG_HandStatus hand in VG_Controller.GetHands())
{
Transform t = VG_Controller.GetBone(hand.m_avatarID, hand.m_side, VG_BoneType.APPROACH, out _, out Matrix4x4 m);
Transform hint = graspHints[num];
hint.gameObject.SetActive(t != null);
if (t != null)
{
hint.SetPositionAndRotation(m.GetColumn(3), Quaternion.LookRotation(m.GetColumn(2), m.GetColumn(1)));
if (m.GetRow(3) != new Vector4(0, 0, 0, 1)) hint.localScale = m.GetRow(3);
hint.GetComponent<Renderer>().material.SetColor("_Color",
VG_Controller.GetHand(hand.m_avatarID, hand.m_side).m_selectedObject == null ? Color.red : Color.green);
}
num++;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,86 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
using UnityEngine.XR;
namespace VirtualGrasp.Scripts
{
/**
* VG_Locomotion does not depends on VG library, and is a script that provides a convenient tool for locomation.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vglocomotion." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_Locomotion : MonoBehaviour
{
public Transform m_character = null;
private Vector2 m_axisL = Vector2.zero;
private Vector2 m_axisR = Vector2.zero;
private Camera m_camera = null;
public float speed = 1.0f;
public float rotationSpeed = 60f;
void TryAssignCamera()
{
if (m_character == null) m_character = transform;
m_camera = GetComponentInChildren<Camera>();
if (m_camera == null) m_camera = Camera.main;
}
void FixedUpdate()
{
if (m_camera == null)
{
TryAssignCamera();
return;
}
if (!InputDevices.GetDeviceAtXRNode(XRNode.LeftHand).TryGetFeatureValue(CommonUsages.primary2DAxis, out m_axisL))
m_axisL = Vector2.zero;
if (!InputDevices.GetDeviceAtXRNode(XRNode.RightHand).TryGetFeatureValue(CommonUsages.primary2DAxis, out m_axisR))
m_axisR = Vector2.zero;
// Key board control
if (Input.GetKey(KeyCode.W))
{
m_axisL.y += 1f;
}
if (Input.GetKey(KeyCode.S))
{
m_axisL.y -= 1f;
}
if (Input.GetKey(KeyCode.A))
{
m_axisL.x -= 1f;
}
if (Input.GetKey(KeyCode.D))
{
m_axisL.x += 1f;
}
if (Input.GetKey(KeyCode.Q))
{
m_character.transform.Translate(new Vector3(0f, -speed * Time.deltaTime, 0f));
}
if (Input.GetKey(KeyCode.E))
{
m_character.transform.Translate(new Vector3(0f, speed * Time.deltaTime, 0f));
}
// Joint stick control from controllers
//float x = Mathf.Abs(m_axisL.x) > Mathf.Abs(m_axisR.x) ? m_axisL.x : m_axisR.x;
//float y = Mathf.Abs(m_axisL.y) > Mathf.Abs(m_axisR.y) ? m_axisL.y : m_axisR.y;
//if (Mathf.Abs(x) > 0.1f) m_character.Rotate(new Vector3(0, 2.0f * x, 0), Space.Self);
//if (Mathf.Abs(y) > 0.1f) m_character.Translate(0.03f * y * (m_camera.transform.rotation * Vector3.forward), Space.World);
// Below has speed control
float x = Mathf.Abs(m_axisL.x) > Mathf.Abs(m_axisR.x) ? m_axisL.x : m_axisR.x;
float y = Mathf.Abs(m_axisL.y) > Mathf.Abs(m_axisR.y) ? m_axisL.y : m_axisR.y;
m_character.Rotate(new Vector3(0, rotationSpeed * x * Time.deltaTime, 0), Space.Self);
m_character.Translate(speed * y * Time.deltaTime * (m_camera.transform.rotation * Vector3.forward), Space.World);
}
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
<<<<<<< HEAD:Assets/ChangeSelectionWeight.cs.meta
guid: c21c93d43a515fc4bb64444425bc4d54
=======
guid: f86eec4236f99b944a3f49c98e34cf45
>>>>>>> master:Assets/ThirdParty/VirtualGrasp/Scripts/Dev/Move.cs.meta
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,106 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using UnityEngine;
namespace VirtualGrasp.Scripts
{
/**
* VG_ObjectAnimator animates a selected object by either rotating around an axis or translate along an axis.
* Useful for to achieve in-hand manipulation of articulated objects.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vgobjectanimator." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_ObjectAnimator : MonoBehaviour
{
[SerializeField, Tooltip("Optional, if not assigned this transform will be used")]
private Transform m_object;
[SerializeField, Tooltip("Which axis to rotate around or translate along")]
private SnapAxis m_axis = SnapAxis.X;
[SerializeField, Tooltip("How much degree to rotate around the axis")]
private float m_angle = 0f;
[SerializeField, Tooltip("How much distance (m) to translate along the axis")]
private float m_distance = 0f;
private Vector3 m_initialLocalPosition = Vector3.zero;
private Quaternion m_initialLocalRotation = Quaternion.identity;
void Awake()
{
if (this.m_object == null)
this.m_object = transform;
}
private void Start()
{
m_initialLocalPosition = this.m_object.localPosition;
m_initialLocalRotation = this.m_object.localRotation;
}
/// <summary>
/// Pass in a float in range [0,1] to drive the translation animation of the object
/// </summary>
public void Translate(float inputValue)
{
inputValue = Mathf.Clamp01(inputValue);
float targetDist = 0f;
float maxDistanceToMove = this.m_distance;
if (inputValue < 0f)
{
inputValue = Mathf.Abs(inputValue);
maxDistanceToMove *= -1f;
}
targetDist = Mathf.Lerp(0f, maxDistanceToMove, inputValue);
var targetPosition = this.m_object.localPosition;
switch (this.m_axis)
{
case SnapAxis.X:
targetPosition.x = targetDist;
break;
case SnapAxis.Y:
targetPosition.y = targetDist;
break;
case SnapAxis.Z:
targetPosition.z = targetDist;
break;
default:
throw new System.NotImplementedException("Axis to rotate not defined");
}
this.m_object.localPosition = m_initialLocalPosition + targetPosition;
}
/// <summary>
/// Pass in a float in range [0,1] to drive the rotation animation of the object
/// </summary>
public void Rotate(float inputValue)
{
inputValue = Mathf.Clamp01(inputValue);
float targetAngle = 0f;
float maxDegreesToRotate = this.m_angle;
if (inputValue < 0f)
{
inputValue = Mathf.Abs(inputValue);
maxDegreesToRotate *= -1f;
}
targetAngle = Mathf.Lerp(0f, maxDegreesToRotate, inputValue);
var targetRotation = this.m_object.localRotation.eulerAngles;
switch (this.m_axis)
{
case SnapAxis.X:
targetRotation.x = targetAngle;
break;
case SnapAxis.Y:
targetRotation.y = targetAngle;
break;
case SnapAxis.Z:
targetRotation.z = targetAngle;
break;
default:
throw new System.NotImplementedException("Axis to rotate not defined");
}
this.m_object.localRotation = Quaternion.Euler(targetRotation) * m_initialLocalRotation;
}
}
}

View File

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

View File

@@ -0,0 +1,303 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
#if VG_ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
using UnityEngine.InputSystem;
#endif
namespace VirtualGrasp.Scripts
{
/**
* VG_Recorder provides a tool to record and replay hand interactions in an object-independent manner.
* The MonoBehavior provides a tutorial on the VG API functions for recording and replaying interactions.
*/
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vgrecorder." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_Recorder : MonoBehaviour
{
public enum RecordingMode
{
RECORD_ON_PLAY,
REPLAY_ON_PLAY,
MANUAL
}
[Tooltip("How to start recording or replaying a sequence; MANUAL means record and replay will be initiated through Keycodes specified.")]
public RecordingMode m_recordingMode = RecordingMode.MANUAL;
//[Tooltip("Reset scene when replay.")]
//public bool m_resetWhenReplay = false;
[Tooltip("Path to save the new Recording asset in. E.g. Assets/Recordings/example.sdb.")]
public string m_newRecordingPath = "Assets/Recordings/example.sdb";
[Tooltip("Recording(s) used for replay")]
public List<VG_Recording> m_replayRecordings = new List<VG_Recording>();
[Tooltip("Avatar used to replay a sensor recording. Has to be the avatar with Replay checked.")]
public List<SkinnedMeshRenderer> m_replayAvatars = null;
[Header("Keycodes")]
#if VG_ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
[Tooltip("Key to press to start and stop recording.")]
public Key m_recordingKey = Key.R;
[Tooltip("Key to press to replay a recording. Shift + Key to Pause/Resume.")]
public Key m_replaySequenceKey = Key.P;
[Tooltip("Key to press to replay a single recording element. Shift + Key to Pause/Resume.")]
public Key m_replaySegmentKey = Key.Y;
#else
[Tooltip("Key to press to start and stop recording. When stop recording most recent recording will be saved to the New Recording Path.")]
public KeyCode m_recordingKey = KeyCode.R;
[Tooltip("Key to press to replay a recording. Shift + Key to Pause/Resume.")]
public KeyCode m_replaySequenceKey = KeyCode.P;
[Tooltip("Key to press to replay a single recording element. Shift + Key to Pause/Resume.")]
public KeyCode m_replaySegmentKey = KeyCode.Y;
#endif
[Header("Segment Replay Options")]
[Tooltip("Avatar's hand to replay a specific interaction segment with.")]
public VG_HandSide m_side = VG_HandSide.LEFT;
[Tooltip("Specific segment to replay (see console output for segment ID, and has to corresond to Side and Replay Object).")]
public int m_segmentID = 5;
[Tooltip("Provide object for segment replay to be centered on this object's current pose.")]
public Transform m_replayObject = null;
private bool m_isRecording = false;
private bool m_isReplaying = false;
private Dictionary<VG_HandSide, bool> m_primaryPushed = new Dictionary<VG_HandSide, bool>() { { VG_HandSide.LEFT, false }, { VG_HandSide.RIGHT, false } };
private Dictionary<VG_HandSide, bool> m_secondaryPushed = new Dictionary<VG_HandSide, bool>() { { VG_HandSide.LEFT, false }, { VG_HandSide.RIGHT, false } };
private Dictionary<VG_HandSide, int> m_joystickPushed = new Dictionary<VG_HandSide, int>() { { VG_HandSide.LEFT, -1 }, { VG_HandSide.RIGHT, -1 } };
private VG_Recording m_tempRecording;
private int m_replayID = 0;
void Start()
{
VG_Controller.OnPostUpdate.AddListener(CheckStopReplay);
switch (m_recordingMode)
{
case RecordingMode.RECORD_ON_PLAY:
TryToggleRecording();
break;
case RecordingMode.REPLAY_ON_PLAY:
StartReplaySequence();
break;
}
if (m_newRecordingPath!=null && m_newRecordingPath.Length > 0)
enforceAppending(ref m_newRecordingPath, ".sdb");
}
private void OnApplicationQuit()
{
if (m_isRecording) VG_Controller.StopRecording();
}
private void enforceAppending(ref string target, string appendingStr)
{
if (target.LastIndexOf(appendingStr) == -1)
{
Debug.LogWarning(target + " is appended with " + appendingStr);
target = target + appendingStr;
return;
}
// Make sure appendingStr is at the end of target
string sub_str = target.Substring(target.LastIndexOf(appendingStr), target.Length - target.LastIndexOf(appendingStr));
if(sub_str.Length != appendingStr.Length)
{
Debug.LogWarning(target + " is appended with " + appendingStr);
target = target + appendingStr;
}
}
void Update() {
#if VG_ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
if (Keyboard.current[m_recordingKey].wasPressedThisFrame) TryToggleRecording();
if (Keyboard.current[m_replaySequenceKey].wasPressedThisFrame) {
if (Keyboard.current[Key.LeftShift].isPressed) ResumePauseReplay();
else StartReplaySequence();
}
if (Keyboard.current[m_replaySegmentKey].wasPressedThisFrame) {
if (Keyboard.current[Key.LeftShift].isPressed) ResumePauseReplay();
else StartReplaySegment();
}
#else
if (Input.GetKeyDown(m_recordingKey)) TryToggleRecording();
if (Input.GetKeyDown(m_replaySequenceKey))
{
if (Input.GetKey(KeyCode.LeftShift)) ResumePauseReplay();
else StartReplaySequence();
}
if (Input.GetKeyDown(m_replaySegmentKey))
{
if (Input.GetKey(KeyCode.LeftShift)) ResumePauseReplay();
else StartReplaySegment();
}
#endif
}
public void TryToggleRecording()
{
if (m_isReplaying)
{
Debug.Log("VG is replaying. Stopping the replay before recording. Try again.");
return;
}
ToggleRecording();
}
private void ToggleRecording()
{
if (!m_isRecording)
{
VG_Controller.StartRecording();
m_isRecording = true;
Debug.Log("Started VG sensor recording, Press Recording Key again will stop recording.");
if (m_newRecordingPath == null || m_newRecordingPath.Length == 0)
Debug.LogWarning("When stop recording, data won't be save to a file because New Recording Path is not set.");
}
else
{
VG_Controller.StopRecording();
m_isRecording = false;
VG_Controller.CollectRecording(out m_tempRecording);
m_replayRecordings.Add(m_tempRecording);
Debug.Log("Stopped VG sensor recording.");
if (m_newRecordingPath == null || m_newRecordingPath.Length == 0)
{
Debug.LogWarning("Recorded sensor data can not be saved to a file because New Recording Path is not set.");
}
else
{
VG_Controller.SaveRecording(m_newRecordingPath);
Debug.Log("Saved VG sensor recording as " + m_newRecordingPath);
#if UNITY_EDITOR
AssetDatabase.ImportAsset(m_newRecordingPath);
#endif
}
}
}
private void CheckStopReplay()
{
if (!m_isReplaying) return;
foreach (int replayAvatarID in GetReplayAvatars())
{
if (!VG_Controller.IsReplaying(replayAvatarID))
{
m_isReplaying = false;
m_replayID++;
if (m_replayID >= m_replayRecordings.Count) m_replayID = 0;
}
}
}
private bool PrepareReplay()
{
if (m_isRecording)
{
Debug.Log("VG is recording. Stop the recording before replay. Try again.");
return false;
}
VG_ReturnCode res;
res = VG_Controller.LoadRecording(m_replayRecordings[m_replayID]);
if (res == VG_ReturnCode.SUCCESS)
{
m_isReplaying = true;
return true;
}
else
{
Debug.LogError("Loading recording failed");
m_isReplaying = false;
return false;
}
}
public void StartReplaySequence()
{
if (!PrepareReplay()) return;
foreach (int replayAvatarID in GetReplayAvatars())
VG_Controller.StartReplay(replayAvatarID);
}
public VG_ReturnCode StartReplaySegment()
{
if (m_replayObject == null)
{
Debug.LogWarning("You need to provide a replay object for StartSingleReplay().");
return VG_ReturnCode.INVALID_TARGET;
}
if (!PrepareReplay()) return VG_ReturnCode.DLL_FUNCTION_FAILED;
foreach (int replayAvatarID in GetReplayAvatars())
if (VG_Controller.StartReplayOnObject(m_replayObject, replayAvatarID, m_side, m_segmentID) != VG_ReturnCode.SUCCESS)
return VG_ReturnCode.ARGUMENT_ERROR;
return VG_ReturnCode.SUCCESS;
}
public void ResumePauseReplay()
{
if (m_isRecording)
{
Debug.Log("VG is recording. Stop the recording before replay. Try again.");
return;
}
if (m_isReplaying)
{
foreach (int replayAvatarID in GetReplayAvatars())
VG_Controller.StopReplay(replayAvatarID);
}
else
{
foreach (int replayAvatarID in GetReplayAvatars())
VG_Controller.ResumeReplay(replayAvatarID);
}
m_isReplaying = !m_isReplaying;
}
private List<int> GetReplayAvatars()
{
List<int> res = new List<int>();
int replayAvatarIDLeft, replayAvatarIDRight;
if (m_replayAvatars == null || m_replayAvatars.Count == 0)
{
if (VG_Controller.GetReplayAvatarID(out replayAvatarIDLeft, out replayAvatarIDRight) != VG_ReturnCode.SUCCESS)
return res;
if(replayAvatarIDLeft == replayAvatarIDRight)
res.Add(replayAvatarIDLeft);
else
{
if(replayAvatarIDLeft != -1)
res.Add(replayAvatarIDLeft);
if(replayAvatarIDRight != -1)
res.Add(replayAvatarIDRight);
}
}
else
{
foreach (SkinnedMeshRenderer sm in m_replayAvatars)
res.Add(sm.GetInstanceID());
}
return res;
}
}
}

View File

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

View File

@@ -0,0 +1,191 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine.XR;
namespace VirtualGrasp.Scripts
{
[LIBVIRTUALGRASP_UNITY_SCRIPT]
[HelpURL("https://docs.virtualgrasp.com/unity_component_vgruntimeregister." + VG_Version.__VG_VERSION__ + ".html")]
public class VG_RunTimeRegister : MonoBehaviour
{
[Serializable]
public class RigRegister
{
[Tooltip("The key to press to clone and register the rig from rigClone.")]
public KeyCode m_registerRigKey = KeyCode.R;
[Tooltip("The rig transform to test rig clone registering.")]
public Transform m_rigClone = null;
[Tooltip("Scale the rig uniformly.")]
public float m_scale = 1.0f;
[Tooltip("The new external controller sensor setup for any new avatar.")]
public VG_SensorSetup m_sensorSetup = null;
}
[Serializable]
public class PrefabRegister
{
[Tooltip("The key to press to clone and register the rig from rigClone.")]
public KeyCode m_registerPrefabKey = KeyCode.P;
[Tooltip("The prefab to test rig clone registering.")]
public GameObject m_prefab = null;
[Tooltip("Scale the prefab uniformly.")]
public float m_scale = 1.0f;
[Tooltip("The new external controller profile for the new avatar.")]
public VG_ControllerProfile m_profile;
[Tooltip("Override the origin of the controller profile. Try to find it by name in the prefab.")]
public string m_originName;
}
[Tooltip("The rigs to register at certain key press events.")]
private List<RigRegister> m_registerRigs = new();
[Tooltip("The rigs to register at certain key press events.")]
public List<PrefabRegister> m_prefabRigs = new();
private Dictionary<int, GameObject> m_prefabClones = new();
[Tooltip("Register the first prefab at start. In case we have no other in the scene.")]
public bool m_registerFirstAvatarAtStart = true;
[Tooltip("If an avatar is instantiated while another is there, remove the older one.")]
public bool m_allowOnlyOneAvatar = false;
private bool m_vrButtonWasPressed = false;
[Header("Register Object")]
[Tooltip("The key to press to clone and register the object from objectClone.")]
public KeyCode m_registerObjectKey = KeyCode.O;
[Tooltip("The object transform to test object clone registering.")]
public Transform m_objectClone = null;
[Header("Register Scene")]
[Tooltip("The key to press to load another scene additively")]
public KeyCode m_registerSceneKey = KeyCode.S;
[Tooltip("The scene name to test scene loading. ActiveScene if left empty.")]
public string m_sceneName = "";
public void Start()
{
if (m_sceneName == "")
m_sceneName = SceneManager.GetActiveScene().name;
if (m_registerFirstAvatarAtStart && m_prefabRigs.Count > 0)
RegisterAvatarAndController(m_prefabRigs[0]);
}
private void RegisterAvatarAndController(RigRegister rig)
{
if (rig.m_rigClone == null || rig.m_rigClone.GetComponentInChildren<SkinnedMeshRenderer>() == null)
return;
GameObject clone = GameObject.Instantiate(rig.m_rigClone.gameObject);
clone.transform.localScale = rig.m_scale * Vector3.one;
if (VG_Controller.RegisterSensorAvatar(clone.GetComponentInChildren<SkinnedMeshRenderer>(), out int id, rig.m_sensorSetup) != VG_ReturnCode.SUCCESS)
{
Destroy(clone);
return;
}
clone.name = clone.name + "_ID" + id;
}
private void UnregisterControlledAvatar()
{
VG_Controller.GetSensorControlledAvatarID(out int avatarID);
if (avatarID != -1)
{
VG_Controller.UnRegisterAvatar(avatarID);
GameObject.Destroy(m_prefabClones[avatarID]);
m_prefabClones.Remove(avatarID);
}
}
private void RegisterAvatarAndController(PrefabRegister prefab)
{
if (m_allowOnlyOneAvatar)
UnregisterControlledAvatar();
GameObject clone = Instantiate(prefab.m_prefab);
if (clone == null || clone.GetComponentInChildren<SkinnedMeshRenderer>() == null)
{
Debug.LogError("Prefab " + prefab.m_prefab.name + " could not be found or has no SkinnedMeshRenderer.", this);
Destroy(clone);
return;
}
clone.transform.localScale = prefab.m_scale * Vector3.one;
VG_SensorSetup sensorSetup = new()
{
m_profile = prefab.m_profile,
m_origin = clone.transform.Find(prefab.m_originName)
};
if (VG_Controller.RegisterSensorAvatar(clone.GetComponentInChildren<SkinnedMeshRenderer>(), out int id, sensorSetup) != VG_ReturnCode.SUCCESS)
{
Destroy(clone);
return;
}
clone.name = prefab.m_prefab.name + "_ID" + id;
m_prefabClones.Add(id, clone);
}
void Update()
{
// Clone an object and register it in runtime to the VG library.
if (Input.GetKeyDown(m_registerObjectKey) && m_objectClone != null)
{
GameObject clone = GameObject.Instantiate(m_objectClone.gameObject, m_objectClone.parent, true);
clone.name = m_objectClone.name + "_clone";
clone.transform.position += 0.05f * Vector3.up;
m_objectClone = clone.transform;
}
// Clone an avatar rig (existing in the scene) and register it in runtime to the VG library.
foreach (RigRegister rig in m_registerRigs)
{
if (Input.GetKeyDown(rig.m_registerRigKey))
RegisterAvatarAndController(rig);
}
// Clone an avatar rig (existing in the scene) and register it in runtime to the VG library.
InputDevice inputDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
foreach (PrefabRegister rig in m_prefabRigs)
{
if (Input.GetKeyDown(rig.m_registerPrefabKey))
RegisterAvatarAndController(rig);
// Register specific avatars with XR X/Y buttons
if (inputDevice.isValid)
{
bool vrButtonIsPressed = false;
if (inputDevice.TryGetFeatureValue(CommonUsages.primaryButton, out vrButtonIsPressed))
{
// Register the one marked with "1" as KeyCode with X
if (rig.m_registerPrefabKey == KeyCode.Alpha1 &&
vrButtonIsPressed && !m_vrButtonWasPressed)
RegisterAvatarAndController(rig);
m_vrButtonWasPressed = vrButtonIsPressed;
}
if (!m_vrButtonWasPressed && inputDevice.TryGetFeatureValue(CommonUsages.secondaryButton, out vrButtonIsPressed))
{
// Register the one marked with "2" as KeyCode with Y
if (rig.m_registerPrefabKey == KeyCode.Alpha2 &&
vrButtonIsPressed && !m_vrButtonWasPressed)
RegisterAvatarAndController(rig);
m_vrButtonWasPressed = vrButtonIsPressed;
}
}
}
if (Input.GetKeyDown(KeyCode.U))
{
VG_Controller.GetSensorControlledAvatarID(out int avatarID);
VG_Controller.UnRegisterAvatar(avatarID);
}
if (Input.GetKeyDown(m_registerSceneKey))
{
SceneManager.LoadScene(m_sceneName, LoadSceneMode.Additive);
}
}
}
}

View File

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

View File

@@ -0,0 +1,442 @@
// Copyright (C) 2014-2024 Gleechi Technology AB. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using VirtualGrasp;
namespace VirtualGrasp.Scripts
{
[CreateAssetMenu(menuName = "VirtualGrasp/VG_Utility")]
public class VG_Utility : ScriptableObject
{
private Transform m_selectedTransform;
private Dictionary<Transform, ArticulationDrive> cachedJointByTransform = new Dictionary<Transform, ArticulationDrive>();
private static VG_HandSide BoolToHandSide(bool isLeft) => isLeft ? VG_HandSide.LEFT : VG_HandSide.RIGHT;
private static int SensorAvatarIDLeft
{
get
{
VG_Controller.GetSensorControlledAvatarID(out var sensorAvatarIDLeft, out var sensorAvatarIDRight);
if (sensorAvatarIDLeft != -1 || sensorAvatarIDRight != -1)
return sensorAvatarIDLeft;
return ReplayAvatarIDLeft;
}
}
private static int SensorAvatarIDRight
{
get
{
VG_Controller.GetSensorControlledAvatarID(out var sensorAvatarIDLeft, out var sensorAvatarIDRight);
if (sensorAvatarIDLeft != -1 || sensorAvatarIDRight != -1)
return sensorAvatarIDRight;
return ReplayAvatarIDRight;
}
}
private static int ReplayAvatarIDLeft
{
get
{
VG_Controller.GetReplayAvatarID(out var replayAvatarIDLeft, out var replayAvatarIDRight);
return replayAvatarIDLeft;
}
}
private static int ReplayAvatarIDRight
{
get
{
VG_Controller.GetReplayAvatarID(out var replayAvatarIDLeft, out var replayAvatarIDRight);
return replayAvatarIDRight;
}
}
public void SelectObject(Transform transform) => m_selectedTransform = transform;
public void SetSelectionWeight(float weight)
{
if (m_selectedTransform == null)
{
Debug.LogError("No transform select in VGUtility");
}
SetSelectionWeight(m_selectedTransform, weight);
}
private void SetSelectionWeight(Transform transform, float weight) => VG_Controller.SetObjectSelectionWeight(transform, weight);
public void SetObjectIsDualHandsOnly(Transform transform)
{
VG_Controller.SetDualHandsOnly(transform, true);
}
public void SetObjectNotDualHandsOnly(Transform transform)
{
VG_Controller.SetDualHandsOnly(transform, false);
}
public void SetJointStateNormalized(float normalizedState)
{
normalizedState = Mathf.Clamp01(normalizedState);
var articulation = FindActiveArticulation(m_selectedTransform);
if (articulation == null)
{
Debug.LogError("No articulation on object, can't set joint state", m_selectedTransform);
return;
}
var state = Mathf.Lerp(articulation.m_min, articulation.m_max, normalizedState);
VG_Controller.SetObjectJointState(m_selectedTransform, state);
}
private static VG_Articulation FindActiveArticulation(Transform transform)
{
var articulations = transform.GetComponents<VG_Articulation>();
foreach (var articulation in articulations)
{
if (articulation.enabled)
{
return articulation;
}
}
return null;
}
public void SetJointState(float state)
{
if (m_selectedTransform == null)
{
Debug.LogError("No transform selected, can't set joint state", this);
return;
}
VG_Controller.SetObjectJointState(m_selectedTransform, state);
}
public void StopSettingJointState()
{
if (m_selectedTransform == null)
{
Debug.LogError("No transform selected, can't set stop set joint state", this);
return;
}
VG_Controller.StopSettingObjectJointState(m_selectedTransform);
}
public void SetGlobalInteractionType(int type)
{
Debug.Log($"Set global interaction type to {(VG_InteractionType)type}");
VG_Controller.SetGlobalInteractionType((VG_InteractionType)type);
}
public void SetInteractionTypeOnSelectedObject(int type)
{
if (this.m_selectedTransform == null)
{
Debug.LogError("No articulation selected, selected one with VGUtility.SelectObject first", this);
return;
}
Debug.Log($"Set interaction type to {(VG_InteractionType)type}");
VG_Controller.SetInteractionTypeForObject(this.m_selectedTransform, (VG_InteractionType)type);
}
public void LockArticulation(Transform transform)
{
if (transform.TryGetComponent<ArticulationBody>(out var articulationBody))
{
if (articulationBody.jointType == ArticulationJointType.FixedJoint) return;
var xDrive = articulationBody.xDrive;
cachedJointByTransform.Add(transform, xDrive);
xDrive.lowerLimit = xDrive.upperLimit = articulationBody.jointPosition[0];
articulationBody.xDrive = xDrive;
}
else
{
VG_Controller.ChangeObjectJoint(transform, VG_JointType.FIXED);
}
}
public void UnLockArticulation(Transform transform)
{
if (transform.TryGetComponent<ArticulationBody>(out var articulationBody))
{
if (cachedJointByTransform.TryGetValue(transform, out var cachedJointType))
{
articulationBody.xDrive = cachedJointType;
cachedJointByTransform.Remove(transform);
}
else
{
Debug.LogWarning("No cached joint found for articulationBody", transform);
}
}
else
{
if (VG_Controller.RecoverObjectJoint(transform) != VG_ReturnCode.SUCCESS)
{
Debug.LogWarning("Couldn't recover articulation");
}
}
}
public void ChangeObjectJoint(VG_Articulation articulation) => VG_Controller.ChangeObjectJoint(articulation.transform, articulation);
public void ChangeObjectJointOnSelectedObject(VG_Articulation articulation)
{
VG_Controller.ChangeObjectJoint(m_selectedTransform, articulation);
}
public void ForceReleaseObject(Transform transform) => VG_Controller.ForceReleaseObject(transform);
public void ForceReleaseObjectLeft()
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not ForceReleaseObjectLeft!");
return;
}
VG_Controller.ForceReleaseObject(left_id, VG_HandSide.LEFT);
}
public void ForceReleaseObjectRight()
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not ForceReleaseObjectRight!");
return;
}
VG_Controller.ForceReleaseObject(right_id, VG_HandSide.RIGHT);
}
public void ForceReleaseObject()
{
int left_id = SensorAvatarIDLeft;
int right_id = SensorAvatarIDRight;
if (left_id != -1)
VG_Controller.ForceReleaseObject(left_id);
if (right_id != left_id && right_id != -1)
VG_Controller.ForceReleaseObject(right_id);
}
public void MakeObjectUngraspable(Transform transform)
{
VG_Controller.SetObjectSelectionWeight(transform, 0f);
}
public void MakeObjectGraspable(Transform transform)
{
VG_Controller.SetObjectSelectionWeight(transform, 1f);
}
public void JumpGraspObjectLeft(Transform transform)
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not JumpGraspObjectLeft!");
return;
}
VG_Controller.JumpGraspObject(left_id, VG_HandSide.LEFT, transform);
}
public void JumpGraspObjectRight(Transform transform)
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not JumpGraspObjectRight!");
return;
}
VG_Controller.JumpGraspObject(right_id, VG_HandSide.RIGHT, transform);
}
public void SwitchGraspObjectLeft(Transform transform)
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not SwitchGraspObjectLeft!");
return;
}
VG_Controller.SwitchGraspObject(left_id, VG_HandSide.LEFT, transform);
}
public void SwitchGraspObjectRight(Transform transform)
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not SwitchGraspObjectRight!");
return;
}
VG_Controller.SwitchGraspObject(right_id, VG_HandSide.RIGHT, transform);
}
public void StartReplay(string replayName)
{
VG_Controller.LoadRecording(replayName);
int left_id = ReplayAvatarIDLeft;
int right_id = ReplayAvatarIDRight;
if (left_id != -1)
VG_Controller.StartReplay(left_id);
if (right_id != left_id && right_id != -1)
VG_Controller.StartReplay(right_id);
}
public void StopReplay()
{
int left_id = ReplayAvatarIDLeft;
int right_id = ReplayAvatarIDRight;
if (left_id != -1)
VG_Controller.StopReplay(left_id);
if (right_id != left_id && right_id != -1)
VG_Controller.StopReplay(right_id);
}
public void SetAvatarMirrorHand(bool mirrorHand)
{
int left_id = SensorAvatarIDLeft;
int right_id = SensorAvatarIDRight;
if (left_id != -1)
VG_Controller.SetAvatarMirrorHandControl(left_id, mirrorHand);
if (right_id != left_id && right_id != -1)
VG_Controller.SetAvatarMirrorHandControl(right_id, mirrorHand);
}
public void SetSensorAvatarActive(bool isActive)
{
int left_id = SensorAvatarIDLeft;
int right_id = SensorAvatarIDRight;
if (left_id != -1)
VG_Controller.SetAvatarActive(left_id, true, isActive, Vector3.down);
if (right_id != left_id && right_id != -1)
VG_Controller.SetAvatarActive(right_id, true, isActive, Vector3.down);
}
public void SetReplayAvatarActive(bool isActive)
{
int left_id = ReplayAvatarIDLeft;
int right_id = ReplayAvatarIDRight;
if (left_id != -1)
VG_Controller.SetAvatarActive(left_id, true, isActive);
if (right_id != left_id && right_id != -1)
VG_Controller.SetAvatarActive(right_id, true, isActive);
}
public void MakeGestureLeft(int gestureID)
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not MakeGestureLeft!");
return;
}
VG_Controller.MakeGesture(left_id, VG_HandSide.LEFT, (VG_GestureType)gestureID);
}
public void MakeGestureRight(int gestureID)
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not MakeGestureRight!");
return;
}
VG_Controller.MakeGesture(right_id, VG_HandSide.RIGHT, (VG_GestureType)gestureID);
}
public void ReleaseGestureLeft()
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not ReleaseGestureLeft!");
return;
}
VG_Controller.ReleaseGesture(left_id, VG_HandSide.LEFT);
}
public void ReleaseGestureRight()
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not ReleaseGestureRight!");
return;
}
VG_Controller.ReleaseGesture(right_id, VG_HandSide.RIGHT);
}
public void ReleaseGesture()
{
int left_id = SensorAvatarIDLeft;
int right_id = SensorAvatarIDRight;
if (left_id != -1)
VG_Controller.ReleaseGesture(left_id, VG_HandSide.LEFT);
if (right_id != -1)
VG_Controller.ReleaseGesture(right_id, VG_HandSide.RIGHT);
}
public void BlockReleaseObjectLeft()
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not BlockReleaseObjectLeft()!");
return;
}
VG_Controller.SetBlockReleaseObject(left_id, VG_HandSide.LEFT, true);
}
public void BlockReleaseObjectRight()
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not BlockReleaseObjectRight!");
return;
}
VG_Controller.SetBlockReleaseObject(right_id, VG_HandSide.RIGHT, true);
}
public void BlockReleaseObject()
{
int left_id = SensorAvatarIDLeft;
int right_id = SensorAvatarIDRight;
if (left_id != -1)
VG_Controller.SetBlockReleaseObject(left_id, VG_HandSide.LEFT, true);
if (right_id != -1)
VG_Controller.SetBlockReleaseObject(right_id, VG_HandSide.RIGHT, true);
}
public void AllowReleaseObjectLeft()
{
int left_id = SensorAvatarIDLeft;
if (left_id == -1)
{
Debug.LogError("No avatar with left hand is registered, can not AllowReleaseObjectLeft()!");
return;
}
VG_Controller.SetBlockReleaseObject(left_id, VG_HandSide.LEFT, false);
}
public void AllowReleaseObjectRight()
{
int right_id = SensorAvatarIDRight;
if (right_id == -1)
{
Debug.LogError("No avatar with right hand is registered, can not AllowReleaseObjectRight!");
return;
}
VG_Controller.SetBlockReleaseObject(right_id, VG_HandSide.RIGHT, false);
}
public void AllowReleaseObject()
{
int left_id = SensorAvatarIDLeft;
int right_id = SensorAvatarIDRight;
if (left_id != -1)
VG_Controller.SetBlockReleaseObject(left_id, VG_HandSide.LEFT, false);
if (right_id != -1)
VG_Controller.SetBlockReleaseObject(right_id, VG_HandSide.RIGHT, false);
}
}
}

View File

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