さわぐちFMオキーステーション

幸せの鐘が鳴り響き僕はただ嬉しいふりをする

Oculus Questのハンドトラッキングでボーンを視覚的に表示する

Oculus Questのハンドトラッキングに割り当てられているボーンを視覚的に表示してみましょう。デバッグやテストをする際に少しわかりやすくなるかも...?

割り当てられているボーンIDを確認する

OVRSkeletonにボーン関連の情報が集約されており、以下のプロパティからIList型でボーンの情報にアクセスできるようです。

skeleteon = GetComponent<OVRSkeleton>();

skeleteon.Bones  ← IList型で取得できる

型はOVRBoneで自身のID、親のインデックス、Transform型を持つ以下のようなクラスとして宣言されています。このクラスを使用すればIDや親子関係も取得できそうです。

public class OVRBone
{
    public OVRSkeleton.BoneId Id { get; private set; }
    public short ParentBoneIndex { get; private set; }
    public Transform Transform { get; private set; }

    public OVRBone(OVRSkeleton.BoneId id, short parentBoneIndex, Transform trans)
    {
        Id = id;
        ParentBoneIndex = parentBoneIndex;
        Transform = trans;
    }
}

で、ボーンIDは以下のように宣言されており、

public enum BoneId
    {
        Invalid                 = -1,

        Hand_Start              = 0,
        Hand_WristRoot          = Hand_Start + 0, // root frame of the hand, where the wrist is located
        Hand_ForearmStub        = Hand_Start + 1, // frame for user's forearm
        Hand_Thumb0             = Hand_Start + 2, // thumb trapezium bone
        Hand_Thumb1             = Hand_Start + 3, // thumb metacarpal bone
        Hand_Thumb2             = Hand_Start + 4, // thumb proximal phalange bone
        Hand_Thumb3             = Hand_Start + 5, // thumb distal phalange bone
        Hand_Index1             = Hand_Start + 6, // index proximal phalange bone
        Hand_Index2             = Hand_Start + 7, // index intermediate phalange bone
        Hand_Index3             = Hand_Start + 8, // index distal phalange bone
        Hand_Middle1            = Hand_Start + 9, // middle proximal phalange bone
        Hand_Middle2            = Hand_Start + 10, // middle intermediate phalange bone
        Hand_Middle3            = Hand_Start + 11, // middle distal phalange bone
        Hand_Ring1              = Hand_Start + 12, // ring proximal phalange bone
        Hand_Ring2              = Hand_Start + 13, // ring intermediate phalange bone
        Hand_Ring3              = Hand_Start + 14, // ring distal phalange bone
        Hand_Pinky0             = Hand_Start + 15, // pinky metacarpal bone
        Hand_Pinky1             = Hand_Start + 16, // pinky proximal phalange bone
        Hand_Pinky2             = Hand_Start + 17, // pinky intermediate phalange bone
        Hand_Pinky3             = Hand_Start + 18, // pinky distal phalange bone
        Hand_MaxSkinnable       = Hand_Start + 19,
        // Bone tips are position only. They are not used for skinning but are useful for hit-testing.
        // NOTE: Hand_ThumbTip == Hand_MaxSkinnable since the extended tips need to be contiguous
        Hand_ThumbTip           = Hand_Start + Hand_MaxSkinnable + 0, // tip of the thumb
        Hand_IndexTip           = Hand_Start + Hand_MaxSkinnable + 1, // tip of the index finger
        Hand_MiddleTip          = Hand_Start + Hand_MaxSkinnable + 2, // tip of the middle finger
        Hand_RingTip            = Hand_Start + Hand_MaxSkinnable + 3, // tip of the ring finger
        Hand_PinkyTip           = Hand_Start + Hand_MaxSkinnable + 4, // tip of the pinky
        Hand_End                = Hand_Start + Hand_MaxSkinnable + 5,

        // add new bones here

        Max = Hand_End + 0,
    }

個々のボーンのIDは以下の図のように割り当てられています。

f:id:amidaMangrove:20200103141132j:plain

ボーンの表示 & 指ごとに色分けしてみる

ボーンを視覚的に表示し指単位で色分けするスクリプトを作ってみました。以下のスクリプトをOVRHandPrefabにアタッチすれば、確認できます。実行時はOVR Mesh Rendererを無効にしておきましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class BoneInfoSample : MonoBehaviour
{
    OVRHand _hand;
    OVRSkeleton _skeleton;
    List<GameObject> _spheres = new List<GameObject>();

    void Start()
    {
        _hand = GetComponent<OVRHand>();
        _skeleton = GetComponent<OVRSkeleton>();

        var boneColor = new Dictionary<string, Color>()
        {
            { "Start",Color.black},  //  スタート位置
            { "Thumb",Color.red},       //  親指
            { "Index",Color.green},     //  人差し指    
            { "Middle",Color.blue},     //  中指
            { "Ring", Color.cyan},      //  薬指
            { "Pinky",Color.magenta},   //  小指
            { "Forearm",Color.yellow},  //  前腕部
        };

        foreach (var bone in _skeleton.Bones) {

            //  Sphereを生成しボーンに割り当てる
            var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            sphere.transform.position = bone.Transform.position;
            sphere.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
            sphere.transform.parent = bone.Transform;

            //  指単位で色を変える
            var color = boneColor.FirstOrDefault(x => bone.Id.ToString().Contains(x.Key));
            sphere.GetComponent<Renderer>().material.color = color.Value;

            _spheres.Add(sphere);
        }
    }

    void Update()
    {
        //  トラックが外れたらSphereを消す
        foreach (var sphere in _spheres) {
            sphere.SetActive(_hand.IsTracked);
        }
            
    }
}

f:id:amidaMangrove:20200103161109p:plain

できました!

OVRSkeletonには「Capsules」が定義されていてここに衝突判定用のCapsule Colliderが入っている予感。次回はこの辺りを見ていこうと思います。