WebXRを利用して、スマートフォンなどのデバイス位置の検出

## さっそくコード

```html:index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>posDetect</title>
  </head>
  <body>
    <canvas class="output-canvas"></canvas>

    <!-- <button class="green button" type="button">Start Game</button> -->
    <button id="xrStartButton" class="blue button use-webxr" type="button">
      Start(VR mode)
    </button>

    <script src="/libs/gl-matrix.js"></script>
    <script>
      const outputCanvas = document.querySelector('.output-canvas');
      const gl = outputCanvas.getContext('webgl');
      const viewerStartPosition = glMatrix.vec3.fromValues(0, 0, -10);
      const viewerStartOrientation = glMatrix.vec3.fromValues(0, 0, 1.0);

      let xrSession = null;
      let usingXR = false;
      let xrStartButton = document.getElementById('xrStartButton');
      let worldRefSpace;
      let viewerRefSpace;
      let animationFrameRequestID;

      window.addEventListener('load', (event) => {
        console.log('window loaded');
        xrStartButton.addEventListener('click', handleStartButtonClick);
      });

      outputCanvas.addEventListener('webglcontextlost', (event) => {
        /* The context has been lost but can be restored */
        event.canceled = true;
      });

      /* When the GL context is reconnected, reload the resources for the
      current scene. */
      outputCanvas.addEventListener('webglcontextrestored', (event) => {
        // loadSceneResources(currentScene);
      });

      function XRRenderingLoop(currentFrameTime, frame) {
        // ループ
        animationFrameRequestID =
          xrSession.requestAnimationFrame(XRRenderingLoop);

        // デバイスの位置取得
        const pose = frame.getViewerPose(viewerRefSpace);
        if (!pose) return;
        console.log(pose.transform.position);
      }

      async function onStartedXRSession(xrSession) {
        xrStartButton.style.visibility = 'hidden';

        try {
          await gl.makeXRCompatible();

          const xrWebGLLayer = new XRWebGLLayer(xrSession, gl);
          xrSession.updateRenderState({ baseLayer: xrWebGLLayer });

          worldRefSpace = await xrSession.requestReferenceSpace('local');
          if (worldRefSpace) {
            viewerRefSpace = worldRefSpace.getOffsetReferenceSpace(
              new XRRigidTransform(viewerStartPosition, viewerStartOrientation)
            );
            // loopスタート
            animationFrameRequestID =
              xrSession.requestAnimationFrame(XRRenderingLoop);
          }
        } catch (err) {
          console.error(err);
          xrSession.end();
        }
      }

      async function handleStartButtonClick(event) {
        if (event.target.classList.contains('use-webxr') && navigator.xr) {
          try {
            xrSession = await navigator.xr.requestSession('immersive-vr'); //immersive-ar
            await onStartedXRSession(xrSession);
            usingXR = true;
          } catch (err) {
            xrSession = null;
            usingXR = false;
          }
        }
      }
    </script>
  </body>
</html>
```

### p5xr使うとすっごい短縮できる

```html:p5xr.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>p5</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.10.0/p5.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/p5.xr/dist/p5xr.js"></script>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <script>
      async function preload() {
        createARCanvas();
        // createVRCanvas();
      }

      function setup() {}

      function draw() {
        const viewer = getViewer();
        console.log(viewer.pose.transform.position);
      }
    </script>
  </body>
</html>

```

## 開発方法Tips
Chromeの拡張機能でエミュレーターがあった
https://chromewebstore.google.com/detail/immersive-web-emulator/cgffilbpcibhmcfbgggfhfolhkfbhmik

いくつかあるけど上記のものは対応機能が多かったのでこれを使ってます

## 実行方法
localhostでデバッグをやるならいいのですが、同じLAN内のスマートフォンだとhttpsじゃないとだ

以前の記事から
https://qiita.com/ono-honeycomb/items/359a254fd4c859692472

```zsh
$ openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
```

```zsh
$ http-server -S -C cert.pem
```

## いろいろ試してみたい！
部屋内でスマートフォンがどこにいて、どこに向けているかってのが検出できそうなので、
部屋のどこの壁の位置にかざしているかだったり、部屋に設置されているどの物を見ているかだったり、ユーザーの首からぶら下げてもらって、ユーザーがどの位置にいるかっていうのを検出したりいろいろできそうです

## 他の検出方法
* ネイティブアプリでもよさそう
* UWBとかの別機器
    * RTKとか
* 壁に測域センサ
    * かざすならNFCでも？
* Kinect的なのでも
    * Femtoとか
    * 今単眼のカメラでも人検出できちゃうのでそれでも
* スマートフォンにQRコード貼っちゃってもいいのか