TrackableImageEmulator を読み解いてみる

概要

NRSDK (Nreal Light の Software Developer Kit) の Emulator を理解するために、 サンプルとして提供されている TrackableImageEmulator (Scene) のソースコードを解読してみます。 個人的な読解結果であるため誤りが含まれている可能性があることをご了承ください。

目次

確認環境

  • NRSDK 1.2.1

解説

まず、TrackableImageEmulator (Scene) を開き Hierarchy を確認しました。 Hierarchy に GameObject と GameObject にアタッチされている Component の一覧を網羅しています。 続いて、網羅した Component に含まれている Script を確認しました。 Scripts に Script の一覧を網羅しています。

網羅するだけでは分かりづらいので、サンプルコードを Application、Emulator、SDKの 3 つに分類します。

  • Application
    • Application に応じて変わるコードやオブジェクト
    • つまり、サンプルの部分になるコードやオブジェクト
  • Emulator
    • Emulator で動かす場合に必要となるコードやオブジェクト
    • Unity Editor の Play mode で動かす
  • SDK
    • Emulator を使わない場合でも必ず必要になるコードやオブジェクト
    • 大体の Application で使うのではと想像する

Game Object を上記の規則で分類すると以下の様になると思います。

  • Application
    • Directional Light
      • 無くても構わない
      • Emulator の Room、Image と Application の Cube を照らす
    • CubeCenter
      • Image Tracking の結果を示すためのオブジェクト
      • Image を認識したら Cube が表示される
  • Emulator
    • TrackableFoundTest
      • TrackableObserver と GameObject を関連づける
      • TrackableObserver の FoundEvent, LostEvent にリスナーを登録する
        • Found では GameObject の位置、方向を設定して有効化する
        • Lost では GameObject を無効化する
      • (Demo に合わせた機能になっている気がしないでもない)
      • (Emulator フォルダには Emulator の Demo も含まれていると考えた方がよいのかも)
    • EmulatorRoom
      • 部屋の Model
      • Emulator で動作させない時には不要になる
    • NRTrackableImageTarget
      • EmulatorRoom 同様、Emulator で動作させない時には不要なオブジェクト(のはず)
      • NRTrackableImageBehavior (Component) では、GameView に表示されている間は TrackingState を Tracking として Emulate する
      • TrackableObserver (Component) では、TrackingState に応じて FoundEvent/LostEvent を呼び出す
        • (Emulator を使わない場合でも必要な処理だと思うのだが、Emulator を使わない場合は書き換えないといけない?)
  • SDK
    • NRCameraRig
      • とにもかくにも必須になるカメラ
      • HMD の位置と方向を追跡する NRHMDPoseTracker (Component) を持つ
      • Emulator として動かすと以下をインスタンス化する
        • Prefabs/NREmulatorManager
        • Prefabs/NREmulatorHeadPose
    • NRInput
      • 入力を扱う為に必須になる
      • GazeTracker, ControllerTracker (Left/Right) により 3 つの Raycast を扱う
      • Laser の描画、Raycast が hit する点の描画を行う
      • デフォルトでは、ControllerTracker (Right) だけが有効
        • NRInput (Component) の DomainHand で Left/Right を指定
        • NRInput (Component) の Raycast Mode で Gaze/Laser を指定
      • Emulator として動かすと以下をインスタンス化する
        • Prefabs/NREmulatorManager
        • Prefabs/NREmulatorController

Hierarchy

※ [C] は Component を意味する。

Scripts

TrackableFoundTest の Scripts

  • TrackableFoundTest (in Emulator/Scripts)

NRTrackableImageTarget の Scripts

  • TrackableObserver (in Emulator/Scripts)
    • NRTrackableImageBehavior の DatabaseIndex を参照して、追跡対象であるかを検証している
    • NRFrame.GetTrackables(List<NRTrackableImage>, NRTrackableQueryFilter.All) を呼び出す
    • NRTrackableImage の TrackingState が Tracking ならば FoundEvent を呼び出し、Tracking 以外ならば LostEvent を呼び出す
    • FoundEvent の呼び出しでは、NRTrackableImage の CenterPose の position と rotation を渡す
      • CubeCenter の position と rotation に使われる
  • NRTrackableImageBehavior : NRTrackableBehaviour (in Emulator/Scripts)
    • NREmulatorManager.IsInGameView で transform (Tracking対象) が GameView に表示されているかを調べる
    • NREmulatorManager.NativeEmulatorApi.UpdateTrackableData() により TrackingState を Tracking/Stopped に切り替える
    • UpdateTrackableData を呼び出す際に、DatabaseIndex (NRTrackableBehaviour のメンバ) を渡している
    • つまり、GameView に表示されていれば画像の追跡は Tracking、表示されていなければ画像の追跡は Stopped として Emulate している

NRCameraRig の Scripts

  • AppManager (in Demos/HelloMR/Scripts)
    • OnEnable/OnDisable で NRInput に ClickListener を追加/削除している
    • ClickListener では HomeButton, AppButton の動作を設定している
    • (NRCameraRig (Prefab) から Demo の Script を参照してしまっていいんですかね?)
  • NRSessionBehavior (Reference) (in Scripts)
    • NSSessionManager を制御するMonoBehavior
      • Awake : CreateSession
      • Start : StartSession
      • OnApplicationPause(true) : DisableSession
      • OnApplicationPause(false) : ResumeSession
      • OnDisable : DisableSession
      • OnDestroy : DestroySession
    • NRSessionConfig で設定できる
      • Start のタイミングで NSSessionManager.SetConfiguration に渡される
  • NSSessionManager (in Scripts/Managers)
    • NRDevice の Init/Destroy
    • NativeTracking, NativeHeadTracking の開始/終了
    • InitEmulator
  • NRHMDPoseTracker (Reference) (in Scripts)
    • GetHeadPose で Pose (position と rotation の組) を取得できる
  • NRMultiDisplayManager (in Scripts/Managers)

NRInput の Scripts

  • NRInput (in Scripts/Input/Controller)
    • Inspector での設定項目
      • Emulate Virtual Display : false
      • Override Camera Center : None (Transform)
      • Anchor Helper
      • Raycast Mode : Laser
      • Click Interval : 0.3
      • Drag Threshold : 0.02
    • 各種入力の取得
    • 各種入力イベントのリスナー登録
    • DomainHand の設定により有効化するコントローラーを指定
    • InitEmulator
  • ControllerAnchorsHelper (in Scripts/Input/Controller)
    • 以下のAnchor を保持する
      • GazePoseTrackerAnchor
      • RightPoseTrackerAnchor
      • LeftPoseTrackerAnchor
      • RightModelAnchor
      • LeftModelAnchor
      • RightLaserAnchor
      • LeftLaserAnchor
  • GazeTracker (in Scripts/Input/Controller)
    • Gaze には注視、凝視という意味がある
    • Inspector での設定項目
      • raycaster に NRPointerRaycaster を設定する
        • GazeRaycaster (GameObject) の NRPointerRaycaster (Component) が設定されている
    • NRInput.OnControllerStatesUpdated に UpdateTracker を登録する
    • UpdateTracker では
      • NRInput.RaycastMode == RaycastModeEnum.Gaze ならば、GazeTracker を有効にする
      • GazeTracker が有効ならば
        • raycaster を有効にする
        • GazeTracker の position, rotation を NRInput.CameraCenter の position, rotation にする
  • ControllerTracker (in Scripts/Input/Controller)
    • Inspector での設定項目
      • defaultHandEnum に Right/Left を設定する
      • raycaster に NRPointerRaycaster を設定する
        • LaserRaycaster (GameObject) の NRPointerRaycaster (Component) が設定されている
      • modelAnchor に ModelAnchor (GameObject) が設定されている
    • NRInput.OnControllerRecentering に OnRecentering を登録する
    • NRInput.OnControllerStatesUpdated に UpdateTracker を登録する
    • OnRecentering では
      • VerifyYAngle の再計算
    • UpdateTracker では
      • NRInput.CheckControllerAvailable(defaultHandEnum) が真ならば、ControllerTracker を有効にする
      • ControllerTracker が有効ならば
        • NRInput.RaycastMode == RaycastModeEnum.Laser ならば、raycaster を有効にする
        • modelAnchor を有効にする
        • TrackPose を呼び出す
    • TrackPose では
      • NRInput.GetControllerAvailableFeature を呼び出して 6 DOF であるかを調べる
      • 6 DOF ならば
        • ControllerTracker の position を NRInput.GetPosition(defaultHandEnum) にする
        • raycaster, modelAnchor の localPosition を zero にする
        • ControllerTracker の localRotation を NRInput.GetRotation(defaultHandEnum) に VerifyYAngle の回転を加えた値にする
      • 6 DOF でなければ
        • ControllerTracker の position を SmoothTrackTargetPosition で計算して設定する
        • raycaster, modelAnchor の localPosition を起動時の初期値にする
        • ControllerTracker の localRotation を NRInput.GetRotation(defaultHandEnum) に VerifyYAngle の回転を加えた値にする
    • SmoothTrackTargetPosition では
      • TargetPos を NRInput.CameraCenter の位置と向きに基づいて計算する
      • ControllerTracker と TargetPos の距離が MaxDistanceFromTarget より大きくなれば、IsMovingToTarget を真にする
      • ControllerTracker と TargetPos の距離が 0.02f より小さくなれば、IsMovingToTarget を偽にする
      • IsMovintToTarget が真であれば、ControllerTracker を TargetPos に近づける
  • NRPointerRaycaster (in Scripts/Input/EventSystem/Raycasters)
    • Inspector での設定項目
      • Near Distance : 0
      • Far Distance : 15
      • Mask Type : Exclusive (/Inclusive)
      • Mask : Nothing (LayerMask value)
      • Show Debug Ray : true
      • Enable Physics Raycast : true
      • Enable Graphic Raycast : true
    • Raycast()
      • Near Distance と Far Distance の間で Ray をとばす
      • sortedRaycastResults に Raycast の結果が保存される
      • breakPoints (要素数 2 の List<Vector3>) に Ray の始点と終点を保存する
        • hit した場合は、終点は hit した位置
        • hit しなかった場合は、Far Distance の位置
    • Raycast(Ray ray, float distance, List raycastResults)
      • Enable Physics Raycast が有効ならば、PhysicsRaycast を呼び出す
      • Enable Graphic Raycast が有効ならば、CanvasTargetCollector.GetCanvases() で得られた全ての ICanvasRaycastTarget に対して GraphicRaycast を呼び出す
    • PhysicsRaycast(Ray ray, float distance, List<RaycastResult> raycastResults)
      • Physics.RaycastNonAlloc に Mask を渡す
      • Mask は Mask Type によって Exclusive/Inclusive を切り替える
    • GraphicRaycast(Canvas canvas, bool ignoreReversedGraphics, Ray ray, float distance, NRPointerRaycaster raycaster, List<RaycastResult> raycastResults)
  • NRLaserReticle (in Scripts/Input/EventSystem)
    • 焦点板、十字線、焦点面につける十字線のことをレチクルと呼ぶらしい
    • Inspector での設定項目
      • Raycaster : NRPointerRaycaster
      • Default Visual : ReticleState.Normal のときに有効にされる GameObject
      • Hover Visual : ReticleState.Hover のときに有効にされる GameObject
      • Default Distance : Raycaster の Near Distance と Far Distance の間に収まる値
        • ReticleState.Normal のときの表示に使われる
      • Reticle Size Ratio
        • localScale の計算に使われる
    • NRInput.ReticleVisualActive が false の場合は、ReticleState.Hide になる
    • Raycaster で hit があれば、ReticleState.Hover になる
      • HitTarget に hit した GameObject が設定される
    • Raycaster で hit がなければ、ReticleState.Normal になる
      • HitTarget は null に設定される
  • NRLaserVisual (in Scripts/Input/EventSystem)
    • Inspector での設定項目
      • Raycaster : NRPointerRaycaster
      • Line Renderer
      • Show On Hit Only
      • Default Distance : Raycaster の Near Distance と Far Distance の間に収まる値
    • NRInput.LaserVisualActive が false の場合は、Line Renderer が無効になる
    • Show On Hit Only が true、かつ、Raycaster で hit がなければ、Line Renderer が無効になる
    • Show On Hit Only が flase、または、Raycaster で hit があれば、Line Renderer で線を描画する
      • 始点は Near Distance の位置
      • 終点は hit した位置、または、Default Distance の位置

NREmulatorManager の Scripts

  • NREmulatorManager (in Emulator/Scripts)
    • NativeEmulatorApi の CreateSIMTracking を呼び出す
    • NativeEmulatorApi の CreateSIMController を呼び出す

NREmulatorHeadPose の Scripts

  • NREmulatorHeadPose (in Emulator/Scripts)
    • キーボード、マウス操作を HMD の動きとして Emulate する

NREmulatorController の Scripts

  • NREmulatorController (in Emulator/Scripts)
    • キーボード、マウス操作を Controller の動きとして Emulate する

参考文献