Files
VIVE-OpenXR-Unity/com.htc.upm.vive.openxr/OpenXRHandTracking/Samples~/Scripts/RenderHand.cs
2023-06-15 10:10:25 +08:00

191 lines
7.7 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VIVE.HandTracking;
namespace VIVE.HandTracking.Sample
{
public class RenderHand : MonoBehaviour
{
// Links between keypoints, 2*i & 2*i+1 forms a link.
// keypoint index: 1: palm, 2-5: thumb, 6-10: index, 11-15: middle, 16-20: ring, 21-25: pinky
// fingers are counted from bottom to top
private static int[] Connections = new int[] {
1, 2, 1, 6, 1, 11, 1, 16, 1, 21, // palm and finger starts
2, 6, 6, 11, 11, 16, 16, 21, // finger starts
2, 3, 3, 4, 4, 5, // thumb
6, 7, 7, 8, 8, 9, 9, 10, // index
11, 12, 12, 13, 13, 14, 14, 15, // middle
16, 17, 17, 18, 18, 19, 19, 20, // ring
21, 22, 22, 23, 23, 24, 24, 25 // pinky
};
[Tooltip("Draw left hand if true, right hand otherwise")]
public bool isLeft = false;
[Tooltip("Use inferred or last-known posed when hand loses tracking if true.")]
public bool allowUntrackedPose = false;
[Tooltip("Default color of hand points")]
public Color pointColor = Color.green;
[Tooltip("Default color of links between keypoints in skeleton mode")]
public Color linkColor = Color.white;
[Tooltip("Material for hand points and links")]
[SerializeField]
private Material material = null;
private XrHandJointsMotionRangeEXT MotionType = XrHandJointsMotionRangeEXT.XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT;
[Tooltip("Type of hand joints range of motion")]
[ReadOnly] public string HandJointsMotionRange;
private List<GameObject> points = new List<GameObject>();
// list of links created (only for skeleton)
private List<GameObject> links = new List<GameObject>();
// Start is called before the first frame update
private XrHandJointLocationEXT[] HandjointLocations = new
XrHandJointLocationEXT[(int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT];
// shared material for all point objects
private Material pointMat = null;
// shared material for all link objects
private Material linkMat = null;
private void Start()
{
HandManager.StartFrameWork(isLeft);
pointMat = new Material(material);
if (isLeft)
{
pointColor = Color.blue;
}
else
{
pointColor = Color.red;
}
pointMat.color = pointColor;
linkMat = new Material(material);
linkMat.color = linkColor;
for (int i = 0; i < (int)XrHandJointEXT.XR_HAND_JOINT_MAX_ENUM_EXT; i++)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.name = ((XrHandJointEXT)i).ToString();
go.transform.parent = transform;
go.transform.localScale = Vector3.one * 0.012f;
go.SetActive(false);
points.Add(go);
go.transform.position = new Vector3((float)i * 0.1f, 0, 0);
// handle layer
go.layer = gameObject.layer;
// handle material
go.GetComponent<Renderer>().sharedMaterial = pointMat;
}
// create game objects for links between keypoints, only used in skeleton mode
for (int i = 0; i < Connections.Length; i += 2)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
go.name = "link" + i;
go.transform.parent = transform;
go.transform.localScale = Vector3.one * 0.005f;
go.SetActive(false);
links.Add(go);
// handle layer
go.layer = gameObject.layer;
// handle material
go.GetComponent<Renderer>().sharedMaterial = linkMat;
}
}
// Update is called once per frame
private void Update()
{
if (HandManager.GetJointLocation(isLeft, out HandjointLocations, ref MotionType))
{
UpdateJointLocation();
switch (MotionType)
{
case XrHandJointsMotionRangeEXT.XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT:
HandJointsMotionRange = "UNOBSTRUCTED";
break;
case XrHandJointsMotionRangeEXT.XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT:
HandJointsMotionRange = "CONTROLLER";
break;
default:
HandJointsMotionRange = "";
break;
}
}
else
{
for (int i = 0; i < points.Count; i++)
{
var go = points[i];
go.SetActive(false);
}
for (int i = 0; i < links.Count; i++)
{
var link = links[i];
link.SetActive(false);
}
}
}
private void UpdateJointLocation()
{
for (int i = 0; i < points.Count; i++)
{
var go = points[i];
XrQuaternionf orientation;
XrVector3f position;
float radius = HandjointLocations[i].radius;
if (allowUntrackedPose) //Use inferred or last-known pose when lost tracking
{
orientation = HandjointLocations[i].pose.orientation;
position = HandjointLocations[i].pose.position;
go.transform.localPosition = new Vector3(position.x, position.y, -position.z);
go.SetActive(true);
}
else
{
if ((HandjointLocations[i].locationFlags & (ulong)XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) != 0)
{
orientation = HandjointLocations[i].pose.orientation;
}
if ((HandjointLocations[i].locationFlags & (ulong)XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_TRACKED_BIT) != 0)
{
position = HandjointLocations[i].pose.position;
go.transform.localPosition = new Vector3(position.x, position.y, -position.z);
go.SetActive(true);
}
else
{
UnityEngine.Debug.Log("Lost tracking");
go.SetActive(false);
}
}
}
for (int i = 0; i < links.Count; i++)
{
var link = links[i];
var pose1 = points[Connections[i * 2]].transform.position;
var pose2 = points[Connections[i * 2 + 1]].transform.position;
// calculate link position and rotation based on points on both end
link.SetActive(true);
link.transform.position = (pose1 + pose2) / 2;
var direction = pose2 - pose1;
link.transform.rotation = Quaternion.FromToRotation(Vector3.up, direction);
link.transform.localScale = new Vector3(0.006f, direction.magnitude / 2f - 0.0051f, 0.006f);
}
}
private void OnDestroy()
{
HandManager.StopFrameWork(isLeft);
UnityEngine.Debug.Log("OnDestroy");
}
//public void setHandVisible(bool isVisible)
//{
// Hand.SetActive(isVisible);
//}
}
}