OculusQeust のハンドトラッキングの摘むを試しみた

Oculus Quest の OVRHand でジェスチャーが取れるかなと思いましたが、どうやら今は摘む（Pinch）ぐらいしか提供されていないようです。そこでまずは各指が摘んでいることがどの程度わかるか、摘んでいるときに指と指がどのくらい離れているのかが正確にわかりそうか試してみました。

# 所感
* 人差し指で摘んだときの精度は、他の指と比較して高そう
* 摘んでいるときの指と指の距離は、連続的に変化しているときは精度が高そう
* まだまだこれから拡張がありそう

# 試してみたこと
## 指で摘む
摘んだ状態が取得できたときには、手の色が変わるようなプログラムを作ってみました。
![pinching.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/10a69f46-ac78-db09-e2d3-60f929369b18.gif)

### 一本指
* 摘んだときには手が黄色に変わるようにしました。
* 人差し指、中指、薬指、小指と変えて試していますが、全てで問題なく判定できていることがわかります。

### 二本指
* 摘んだときには手が紫色（デフォルト）に変わるようにしました。（つまり、一本で黄色、二本で戻るようにしています。が、きちんとやるなら、二本で別の色にしないとロストしているのか、二本と認識しているのかわからなかったですね・・・）
* 人差し指で摘んだときに黄色になっていますが、中指も合わせて摘むと紫色になっていることがわかります。
* 他の指でもおおよそ動作していますが、指によっては精度が甘くなることがあるようでした。
* 見えている角度によるのかなと感じていますが、人差し指付近の精度が一番高いように感じました。

## 指と親指の距離
各指と親指との間で、ある箇所から完全にくっつくところまで、0.0〜1.0（0.0がくっついた値）の値で取得することができます。この値を元にキューブのサイズを変更（完全にくっつくと消失する）してみました。

![pinching_strength.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/860e3996-b8bf-3587-3965-fc16e734a7cf.gif)

1. 摘んでいない状態、摘んだ状態での値は正確で、キューブの変化はないあるいは、キューブが完全に消えた状態になります。
2. 連続的に変化させている状態だと、自然な変化が見られる
3. 途中で指を止めても、トラッキングのブレから値（キューブ）の変化が見られる。最後の4秒程度は指を全く動かしていませんが、キューブのサイズが変わっているのがわかるかと思います。

# サンプルのコード
```C#
using UnityEngine;

public class Detection : MonoBehaviour
{
    [SerializeField] private OVRHand ovrHand;
    [SerializeField] private SkinnedMeshRenderer handRenderer;

    [SerializeField] private Material on;
    [SerializeField] private Material off;
    [SerializeField] private Transform cube;

    private bool _isIndexPinching;
    private bool _isMiddlePinching;
    private bool _isRingPinching;
    private bool _isPinkyPinching;
    private int _pinchingFingerNumber;
    
    void Update()
    {
        _isIndexPinching = ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Index);
        _isMiddlePinching = ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Middle);
        _isRingPinching = ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Ring);
        _isPinkyPinching = ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Pinky);

        _pinchingFingerNumber = 0;
        if (_isIndexPinching) _pinchingFingerNumber++;
        if (_isMiddlePinching) _pinchingFingerNumber++;
        if (_isRingPinching) _pinchingFingerNumber++;
        if (_isPinkyPinching) _pinchingFingerNumber++;

        // 一本指でピンチしているときだけ on にする
        handRenderer.materials = _pinchingFingerNumber == 1 ? new[] {on} : new[] {off};
        
        // 人差し指のピンチングの強さに応じて cube の scale を変更する
        var strength = ovrHand.GetFingerPinchStrength(OVRHand.HandFinger.Index);
        var scale = 0.075f * (1 - strength);
        cube.localScale = new Vector3(scale, scale, scale);
    }
}

```

# 補足
```C#
public enum HandStatus
{
    HandTracked = (1 << 0), // if this is set the hand pose and bone rotations data is usable
    InputStateValid = (1 << 1), // if this is set the pointer pose and pinch data is usable
    SystemGestureInProgress = (1 << 6), // if this is set the hand is currently processing a system gesture
}
```
OVRPlugin の中を見ると、 `HandStatus` の `SystemGestureInProgress` の値が、飛び石で設定されています。この間に何か入ってくるのではないかと思われます。また `BoneId` にあるコメントも、


```
		// add new bones here
```
とあり、まだまだこれから色々な拡張が入ってきそうだなと感じました。

# 参考
[OculusQuestのハンドトラッキングについて色々調べてみた](https://qiita.com/Horukasu/items/eb1bc20621d51a85d988)