├── OpenVRoff.png ├── .gitignore ├── README.md ├── EasyOpenVRDashboardForUnity.cs └── EasyOpenVROverlayForUnity.cs /OpenVRoff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpsnmeajp/EasyOpenVROverlayForUnity/HEAD/OpenVRoff.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Mm]emoryCaptures/ 12 | 13 | # Asset meta data should only be ignored when the corresponding asset is also ignored 14 | !/[Aa]ssets/**/*.meta 15 | 16 | # Uncomment this line if you wish to ignore the asset store tools plugin 17 | # /[Aa]ssets/AssetStoreTools* 18 | 19 | # Autogenerated Jetbrains Rider plugin 20 | [Aa]ssets/Plugins/Editor/JetBrains* 21 | 22 | # Visual Studio cache directory 23 | .vs/ 24 | 25 | # Gradle cache directory 26 | .gradle/ 27 | 28 | # Autogenerated VS/MD/Consulo solution and project files 29 | ExportedObj/ 30 | .consulo/ 31 | *.csproj 32 | *.unityproj 33 | *.sln 34 | *.suo 35 | *.tmp 36 | *.user 37 | *.userprefs 38 | *.pidb 39 | *.booproj 40 | *.svd 41 | *.pdb 42 | *.mdb 43 | *.opendb 44 | *.VC.db 45 | 46 | # Unity3D generated meta files 47 | *.pidb.meta 48 | *.pdb.meta 49 | *.mdb.meta 50 | 51 | # Unity3D generated file on crash reports 52 | sysinfo.txt 53 | 54 | # Builds 55 | *.apk 56 | *.unitypackage 57 | 58 | # Crashlytics generated file 59 | crashlytics-build.properties 60 | 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyOpenVROverlayForUnity 2 | OepnVRを用いたオーバーレイ表示の支援スクリプトです。 3 | 4 | CC0ライセンスです。 5 | バグ報告などは、メール、もしくはDiscordまで 6 | 7 | 8 | # EasyOpenVROverlayForUnity.cs 9 | ![](https://sabowl.sakura.ne.jp/gpsnmeajp/unity/EasyOpenVROverlayForUnity/shot1.png) 10 | 11 | OepnVRを用いたオーバーレイ表示の支援スクリプトです。 12 | Unity2018.2.5f1 Personalで動作確認済です。 13 | 14 | 任意のSteamVRゲームのVR空間内に割り込んで自分のアプリケーションのRenderTextureを出せます。 15 | 機能の仕組み上、表示は2Dです。 16 | 17 | 1スクリプトというシンプルな構成で、なるべく簡単に使えることを目指しました。 18 | OVRDropのような入力機能は、うまく動作しないので省いています。 19 | 20 | 注意: 実行ファイル生成時のopenvr_api.dllの同梱し忘れにはご注意ください 21 | 22 | v0.24 OpenGL環境で正常に動作しない問題修正 23 |  showDevicesでエラーが発生することがあるためコメントアウト 24 | v0.23 Side-By-Side 3D対応 25 | v0.22 デバイスアップデート 26 | ・デバイス詳細情報をログやInspectorに表示するように(シリアル番号など) 27 | ・デバイスを8まで選択可能に(ベースステーションやトラッカーが使用できるように) v0.21 微修正 28 | v0.2 大規模更新 2018-09-23 29 | ・デバッグタグの方法を変更 30 | ・uGUIのクリックに対応 31 | ・コントローラーを選択できるように 32 | ・外部からのエラーチェック、表示状態管理関数を追加。 33 | ・各処理を関数化 34 | ・終了時に開放する処理を追加 35 | ・エラー時に開放する処理を追加 36 | ・マウススケールの処理を追加 37 | ・終了イベントのキャッチを追加 38 | ・デモアプリケーションを追加 39 | v0.1 公開 40 | 41 | ## こちらのQiita記事もどうぞ 42 | [VRゲームにオーバーレイ表示したい人向けサンプル(OpenVR Overlay)](https://qiita.com/gpsnmeajp/items/421e3853465df3b1520b) 43 | [VaNiiMenuみたいに空間タッチできるオーバーレイアプリケーションの作り方](https://qiita.com/gpsnmeajp/items/3b67223f7f11bb6d93c3) 44 | 45 | ## 使い方 46 | ## Unity2020.3.3f1の場合 47 | 1. [OpenVR SDK](https://github.com/ValveSoftware/openvr)をダウンロード 48 | 2. Unityを立ち上げ、3Dプロジェクトを新規作成 49 | 3. Pluginsフォルダを作り、そこに「openvr-1.16.8\bin\win64\openvr_api.dll」「openvr-1.16.8\headers\openvr_api.cs」を入れる 50 | 4. OpenXRが有効なら切る 51 | 5. EasyOpenVROverlayForUnity.csをインポートします。 52 | 6. エラー箇所をコメントアウトする(後々直します) 53 | 7. Assetsを右クリックし、Create→Render Texture。「New Render Texture」ができる。 54 | 8. HierarchyのMain Cameraの「Target Texture」に、「New Render Texture」をドラッグアンドドロップしてセット 55 | 9. Hierarchyを右クリックしてCreate Empty 56 | 10. 出来上がったGameObjectにEasyOpenVROverlayForUnityをドラッグアンドドロップ 57 | 11. GameObjectをクリック 58 | 12. EasyOpenVROverlayForUnityのRender Textureに「New Render Texture」をドラッグアンドドロップしてセット 59 | 60 | 61 | 以下古い手順 62 | 63 | ### Unityプロジェクトの準備(Unity2018.2.5f1 Personal) 64 | 1. Unityを立ち上げ、3Dプロジェクトを新規作成します 65 | 2. Asset StoreからSteam VR Pluginをインポートします 66 | 3. SteamVR_Settingsが立ち上がるので、Accept All 67 | 4. Edit→Project Settings→Playerを開き、Inspectorの下のXR SettingsからVirtual Reality Supportedをオフに 68 | 5. Edit→Preferencesを開き、SteamVRのAutomativally Enable VRをオフに 69 | ![](https://github.com/gpsnmeajp/EasyOpenVROverlayForUnity/blob/main/OpenVRoff.png?raw=true) 70 | 71 | 6. EasyOpenVROverlayForUnity.csをインポートします。 72 | 7. Assetsを右クリックし、Create→Render Texture。「New Render Texture」ができる。 73 | 8. HierarchyのMain Cameraの「Target Texture」に、「New Render Texture」をドラッグアンドドロップしてセット 74 | 9. Hierarchyを右クリックしてCreate Empty 75 | 10. 出来上がったGameObjectにEasyOpenVROverlayForUnityをドラッグアンドドロップ 76 | 11. GameObjectをクリック 77 | 12. EasyOpenVROverlayForUnityのRender Textureに「New Render Texture」をドラッグアンドドロップしてセット 78 | 79 | 動作確認は、SteamVRを起動後、Unityの再生ボタンをクリック。 80 | Consoleに「[EasyOpenVROverlayForUnity]初期化完了しました」と表示され、HMD内に先ほどまでのUnityの画面みたいなのが中央に出ていればOK。 81 | この場合MainCameraはディスプレイ出力をしなくなるため、「Display 1 No cameras rendering」と出るが問題ない 82 | 83 | 84 | ### uGUIのクリックについて 85 | 使い方 86 | 1. LaycastRootObjectには、操作したいCanvas(シーン直下に配置)を設定 87 | 2. Buttonのクリックだけ対応(コントローラーの先端でOverlayを叩くとクリック) 88 | 3. ButtonのRaycast TargetはONに。ButtonのTextにあるRaycast TargetはOFFに 89 | 4. CanvasのRender Modeは"Screen Space - Camera"に。 90 | 5. CanvasのRender Cameraは、RenderTextureを設定したCameraと同じものにすること 91 | なお、LaycastRootObjectをnull(None)にするとGUI機能は無効化される 92 | 93 | 設定 94 | ![](https://sabowl.sakura.ne.jp/gpsnmeajp/unity/EasyOpenVROverlayForUnity/set2.png) 95 | 96 | 設定項目について解説します。 97 | 🔍がついているものはInspector上での表示あるいはスクリプトからの取得用です。 98 | 操作しても意味がありません 99 | 🔧がついているものはInspector上でのデバッグ操作用です。 100 | 101 | 🔍Error エラー状態を示します。チェックが入っていると何らかのエラーで動作を停止しています。 102 | 🔧Event Log Open VRの出力する大量のイベント情報をログに出力します。 103 | 104 | **Render Texture** 105 | **Render Texture** VR空間に描画するRenderTextureを指定します。 106 | 107 | **Transform** 108 | **Position** VR空間内での位置を指定します。 109 | **Rotation** VR空間内での回転を指定します。Quadなので+-90度以上(裏面)は見えなくなります。 110 | **Scale** VR空間内での拡大率を指定します。(非推奨。大きさ調整はwidthを推奨) 111 | **Mirror** X X方向の鏡像反転 112 | **Mirror** Y Y方向の鏡像反転 113 | 114 | **Setting** 115 | **Width** 幅の大きさをm単位で指定します。高さはTextureの比から自動で計算されます。 116 | **Alpha** 全体の透明度を指定します。0が完全に透明、1が不透明 117 | **Show** 表示非表示を切り替えます。 118 | 119 | **Name** 120 | **Overlay Friendly Name** ユーザーに表示する用の名前を設定します。 121 | **Overlay Key Name OpenVR**システムが識別する用の名前を設定します。 122 |  (この名前が同じオーバーレイは同時に表示できないため、必ず変更してください) 123 | 124 | **DeviceTracking** 125 | **Device Tracking** デバイスからの相対位置か、ルーム内絶対位置にするか設定します。 126 | **Device Index** デバイス番号を指定します。Left/Right Controllerは変動する値に自動追従します 127 | 128 | **Absolute space** (この項目はDevice TrackingがOFFのときのみ有効です。) 129 | **Seated** ルームスケールか着座スケールかを選択します 130 | **🔧ResetSeatedCamera** 着座スケールの際、カメラ位置をリセットします 131 |  (リセット完了後自動でOFFになります) 132 | 133 | **Device Info** 134 | **🔧Put Log Devices Info** 現在接続されているデバイス一覧をログに出力します。 135 |  (出力完了後自動でOFFになります) 136 | **🔍Connected Devices Device** Indexが切り替えられたタイミングでの接続デバイス数 137 | **🔍Selected Device Index Device** Indexで選択されているデバイスのIndex 138 | **🔍Device Serial Number Device** Indexで選択されているデバイスのシリアル番号 139 | **🔍Device Render Model Name Device** Indexで選択されているデバイスの機種情報 140 | 141 | **GUI Tap** 142 | **Laycast Root Object** 操作対象のCanvas(シーン直下に配置してください) 143 | **🔍tappedLeft** 左コントローラーでタップされている間ON 144 | **🔍tappedRight** 右コントローラーでタップされている間ON 145 | **TapOnDistance** タップされていると判定する距離 146 | **TapOffDistance** タップが終わったと判定する距離 147 | **🔍LeftHandU** 左コントローラーでタップされているU座標 148 | **🔍LeftHandV** 左コントローラーでタップされているV座標 149 | **🔍LeftHandDistance** 左コントローラーの距離 150 | **🔍RightHandU** 右コントローラーでタップされているU座標 151 | **🔍RightHandV** 右コントローラーでタップされているV座標 152 | **🔍RightHandDistance** 右コントローラーの距離 153 | -------------------------------------------------------------------------------- /EasyOpenVRDashboardForUnity.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * EasyOpenVRDashboardForUnity by gpsnmeajp v0.1 3 | * 2018/09/02 4 | * 5 | * v0.1 公開 6 | * These codes are licensed under CC0. 7 | * http://creativecommons.org/publicdomain/zero/1.0/deed.ja 8 | */ 9 | 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | using UnityEngine; 13 | using Valve.VR; //Steam VR 14 | 15 | public class EasyOpenVRDashboardForUnity : MonoBehaviour 16 | { 17 | //エラーメッセージの名前 18 | const string Tag = "[EasyOpenVRDashboardForUnity]"; 19 | 20 | //-------------------------------------------------------------------------- 21 | [Header("thumbnail (Please check [Advanced] -> [Read/Write Enabled])")] 22 | //サムネイルテクスチャ 23 | public Texture2D thumbnailTextureInput; 24 | 25 | [Header("RenderTexture")] 26 | //取得元のRenderTexture 27 | public RenderTexture renderTexture; 28 | 29 | [Header("Setting")] 30 | //オーバーレイの大きさ設定(幅のみ。高さはテクスチャの比から自動計算される) 31 | [Range(0, 100)] 32 | public float width = 1.5f; 33 | 34 | //オーバーレイの透明度を設定 35 | [Range(0, 1)] 36 | public float alpha = 0.2f; 37 | 38 | //表示するか否か 39 | public bool show = true; 40 | 41 | [Header("Name")] 42 | //ユーザーが確認するためのオーバーレイの名前 43 | public string OverlayFriendlyName = "SampleOverlay"; 44 | 45 | //グローバルキー(システムのオーバーレイ同士の識別名)。 46 | //ユニークでなければならない。乱数やUUIDなどを勧める 47 | public string OverlayKeyName = "SampleOverlay"; 48 | 49 | [Header("Error")] 50 | public bool error = false; 51 | public bool eventlog = true; 52 | 53 | [Header("Mouse (Read only)")] 54 | public float MouseX = 0.0f; 55 | public float MouseY = 0.0f; 56 | public bool MouseClick=false; 57 | 58 | 59 | //-------------------------------------------------------------------------- 60 | 61 | //オーバーレイのハンドル(整数) 62 | ulong overlayHandle = 0; 63 | ulong thumbnailHandle = 0; 64 | 65 | //OpenVRシステムインスタンス 66 | #pragma warning disable 0414 67 | CVRSystem openvr; 68 | #pragma warning restore 0414 69 | 70 | //Overlayインスタンス 71 | CVROverlay overlay; 72 | 73 | //オーバーレイに渡すネイティブテクスチャ 74 | Texture_t overlayTexture; 75 | Texture_t thumbnailTexture; 76 | 77 | //サムネイルの加工用テクスチャ領域 78 | Texture2D thumbnailTexture2D; 79 | 80 | //サムネイルが利用可能か 81 | bool thumbnailAvailable = false; 82 | 83 | //入力イベント取得用イベント 84 | VREvent_t Event; 85 | 86 | //テスクチャ領域設定 87 | VRTextureBounds_t OverlayTextureBounds; 88 | 89 | HmdVector2_t vecMouseScale; 90 | 91 | void Start() 92 | { 93 | var openVRError = EVRInitError.None; 94 | var overlayError = EVROverlayError.None; 95 | 96 | //OpenVRの初期化 97 | openvr = OpenVR.Init(ref openVRError, EVRApplicationType.VRApplication_Overlay); 98 | if (openVRError != EVRInitError.None) 99 | { 100 | Debug.LogError(Tag + "OpenVRの初期化に失敗." + openVRError.ToString()); 101 | error = true; 102 | return; 103 | } 104 | 105 | //オーバーレイ機能の初期化 106 | overlay = OpenVR.Overlay; 107 | overlayError = overlay.CreateDashboardOverlay(OverlayKeyName, OverlayFriendlyName, ref overlayHandle, ref thumbnailHandle); 108 | if (overlayError != EVROverlayError.None) 109 | { 110 | Debug.LogError(Tag + "Overlayの初期化に失敗. " + overlayError.ToString()); 111 | error = true; 112 | return; 113 | } 114 | 115 | //マウス(Dashboardのときのみ有効) 116 | overlay.SetOverlayInputMethod(overlayHandle, VROverlayInputMethod.Mouse); 117 | 118 | //オーバーレイに渡すテクスチャ種類の設定 119 | OverlayTextureBounds = new VRTextureBounds_t(); 120 | var isOpenGL = SystemInfo.graphicsDeviceVersion.Contains("OpenGL"); 121 | if (isOpenGL) 122 | { 123 | //pGLuintTexture 124 | overlayTexture.eType = ETextureType.OpenGL; 125 | thumbnailTexture.eType = ETextureType.OpenGL; 126 | //上下反転しない 127 | OverlayTextureBounds.uMin = 1; 128 | OverlayTextureBounds.vMin = 0; 129 | OverlayTextureBounds.uMax = 1; 130 | OverlayTextureBounds.vMax = 0; 131 | overlay.SetOverlayTextureBounds(thumbnailHandle, ref OverlayTextureBounds); 132 | } 133 | else 134 | { 135 | //pTexture 136 | overlayTexture.eType = ETextureType.DirectX; 137 | thumbnailTexture.eType = ETextureType.DirectX; 138 | //上下反転する 139 | OverlayTextureBounds.uMin = 0; 140 | OverlayTextureBounds.vMin = 1; 141 | OverlayTextureBounds.uMax = 1; 142 | OverlayTextureBounds.vMax = 0; 143 | overlay.SetOverlayTextureBounds(overlayHandle, ref OverlayTextureBounds); 144 | } 145 | 146 | //サムネイルテクスチャが存在するなら 147 | if (thumbnailTextureInput != null) 148 | { 149 | //サムネイルテクスチャの情報を取得 150 | var thumWidth = thumbnailTextureInput.width; 151 | var thumHeight = thumbnailTextureInput.height; 152 | //作業用テクスチャの領域を確保 153 | thumbnailTexture2D = new Texture2D(thumWidth, thumHeight, TextureFormat.RGBA32, false); 154 | 155 | //力技でコピーして圧縮を解除 156 | if (isOpenGL) 157 | { 158 | for (int y = 0; y < thumHeight; y++) 159 | { 160 | for (int x = 0; x < thumWidth; x++) 161 | { 162 | thumbnailTexture2D.SetPixel(x, y, thumbnailTextureInput.GetPixel(x, y)); 163 | } 164 | } 165 | } 166 | else { 167 | //DirectXは上下反転 168 | for (int y = 0; y < thumHeight; y++) 169 | { 170 | for (int x = 0; x < thumWidth; x++) 171 | { 172 | thumbnailTexture2D.SetPixel(x, thumHeight-y-1, thumbnailTextureInput.GetPixel(x, y)); 173 | } 174 | } 175 | } 176 | 177 | //操作を反映 178 | thumbnailTexture2D.Apply(); 179 | //ネイティブテクスチャを取得して設定 180 | thumbnailTexture.handle = thumbnailTexture2D.GetNativeTexturePtr(); 181 | //サムネイル利用可能フラグを立てる 182 | thumbnailAvailable = true; 183 | } 184 | 185 | Debug.Log(Tag + "初期化完了しました"); 186 | } 187 | 188 | void Update() 189 | { 190 | //初期化失敗するなどoverlayが無効な場合は実行しない 191 | if (overlay == null || error) 192 | { 193 | return; 194 | } 195 | 196 | if (show) 197 | { 198 | //オーバーレイを表示する 199 | overlay.ShowOverlay(overlayHandle); 200 | } 201 | else 202 | { 203 | //オーバーレイを非表示にする 204 | overlay.HideOverlay(overlayHandle); 205 | } 206 | 207 | //イベントを処理する 208 | uint uncbVREvent = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VREvent_t)); 209 | while (overlay.PollNextOverlayEvent(overlayHandle, ref Event, uncbVREvent)) 210 | { 211 | if (eventlog) 212 | { 213 | Debug.Log(Tag + "Event:" + ((EVREventType)Event.eventType).ToString()); 214 | } 215 | //Debug.Log(Tag +"Event:"+ (Event.eventType).ToString()); 216 | switch ((EVREventType)Event.eventType) 217 | { 218 | case EVREventType.VREvent_MouseMove: 219 | MouseX = Event.data.mouse.x; 220 | MouseY = Event.data.mouse.y; 221 | break; 222 | case EVREventType.VREvent_MouseButtonDown: 223 | MouseClick = true; 224 | break; 225 | case EVREventType.VREvent_MouseButtonUp: 226 | MouseClick = false; 227 | break; 228 | case EVREventType.VREvent_DashboardActivated: 229 | break; 230 | case EVREventType.VREvent_DashboardDeactivated: 231 | break; 232 | case EVREventType.VREvent_DashboardRequested: 233 | break; 234 | case EVREventType.VREvent_DashboardThumbSelected: 235 | break; 236 | case EVREventType.VREvent_EnterStandbyMode: 237 | break; 238 | case EVREventType.VREvent_LeaveStandbyMode: 239 | break; 240 | case EVREventType.VREvent_KeyboardCharInput: 241 | break; 242 | case EVREventType.VREvent_KeyboardClosed: 243 | break; 244 | case EVREventType.VREvent_KeyboardDone: 245 | break; 246 | case EVREventType.VREvent_ResetDashboard: 247 | break; 248 | case EVREventType.VREvent_ScreenshotTriggered: 249 | break; 250 | case EVREventType.VREvent_WirelessDisconnect: 251 | break; 252 | case EVREventType.VREvent_WirelessReconnect: 253 | break; 254 | case EVREventType.VREvent_Quit: 255 | Debug.Log(Tag + "Quit"); 256 | ApplicationQuit(); 257 | break; 258 | default: 259 | break; 260 | } 261 | } 262 | 263 | if (overlay.IsDashboardVisible()) 264 | { 265 | //サムネイルにテクスチャを設定 266 | if (thumbnailAvailable) 267 | { 268 | var overlayError = EVROverlayError.None; 269 | overlayError = overlay.SetOverlayTexture(thumbnailHandle, ref thumbnailTexture); 270 | if (overlayError != EVROverlayError.None) 271 | { 272 | Debug.LogError(Tag + "Overlayにサムネイルをセットできませんでした. " + overlayError.ToString()); 273 | error = true; 274 | return; 275 | } 276 | } 277 | } 278 | 279 | //オーバーレイが表示されている時 280 | if (overlay.IsOverlayVisible(overlayHandle) && overlay.IsActiveDashboardOverlay(overlayHandle)) 281 | { 282 | //オーバーレイの大きさ設定(幅のみ。高さはSetOverlayMouseScaleの比から自動計算される) 283 | overlay.SetOverlayWidthInMeters(overlayHandle, width); 284 | //オーバーレイの透明度を設定 285 | overlay.SetOverlayAlpha(overlayHandle, alpha); 286 | 287 | //RenderTextureが生成されているかチェック 288 | if (!renderTexture.IsCreated()) 289 | { 290 | Debug.Log(Tag + "RenderTextureがまだ生成されていない"); 291 | return; 292 | } 293 | 294 | try 295 | { 296 | //マウスカーソルスケールを設定する(これにより表示領域のサイズも決定される) 297 | vecMouseScale.v0 = renderTexture.width; 298 | vecMouseScale.v1 = renderTexture.height; 299 | overlay.SetOverlayMouseScale(overlayHandle, ref vecMouseScale); 300 | //RenderTextureからネイティブテクスチャのハンドルを取得 301 | overlayTexture.handle = renderTexture.GetNativeTexturePtr(); 302 | } 303 | catch (UnassignedReferenceException e) 304 | { 305 | Debug.LogError(Tag + "RenderTextureがセットされていません "+e.ToString()); 306 | error = true; 307 | return; 308 | } 309 | 310 | //オーバーレイにテクスチャを設定 311 | var overlayError = EVROverlayError.None; 312 | overlayError = overlay.SetOverlayTexture(overlayHandle, ref overlayTexture); 313 | if (overlayError != EVROverlayError.None) 314 | { 315 | Debug.LogError(Tag + "Overlayにテクスチャをセットできませんでした. " + overlayError.ToString()); 316 | error = true; 317 | return; 318 | } 319 | } 320 | } 321 | 322 | private void OnDestroy() 323 | { 324 | //アプリケーション終了時にOverlayハンドルを破棄する 325 | if (overlay != null) 326 | { 327 | overlay.DestroyOverlay(overlayHandle); 328 | } 329 | } 330 | 331 | //アプリケーションを終了させる 332 | void ApplicationQuit() 333 | { 334 | #if UNITY_EDITOR 335 | UnityEditor.EditorApplication.isPlaying = false; 336 | #else 337 | Application.Quit(); 338 | #endif 339 | } 340 | } -------------------------------------------------------------------------------- /EasyOpenVROverlayForUnity.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * EasyOpenVROverlayForUnity by gpsnmeajp v0.24 3 | * 2019/07/15 4 | * 5 | * v0.24 OpenGL環境で正常に動作しない問題修正 6 | * showDevicesでエラーが発生することがあるためコメントアウト 7 | * 8 | * v0.23 Side-by-Side 3D対応 9 | * 10 | * v0.22 デバイスアップデート 11 | * デバイス情報を取得して一覧をログに出すように 12 | * 選択したデバイスの詳細情報を取得できるように 13 | * トラッカーやベースステーションの位置にオーバーレイを出せるように 14 | * Tagに対するWarnningsを抑制 15 | * 16 | * v0.21 微修正 17 | * v0.2 大規模更新 18 | * デバッグタグの方法を変更 19 | * uGUIのクリックに対応 20 | * コントローラーを選択できるように 21 | * 外部からのエラーチェック、表示状態管理関数を追加。 22 | * 各処理を関数化 23 | * 終了時に開放する処理を追加 24 | * エラー時に開放する処理を追加 25 | * マウススケールの処理を追加 26 | * 終了イベントのキャッチを追加 27 | * v0.1 公開 2018/08/25 28 | * 29 | * 2DのテクスチャをVR空間にオーバーレイ表示します。 30 | * 現在動作中のアプリケーションに関係なくオーバーレイすることができます。 31 | * 32 | * 入力機能は正常に動作していないようなので省いています。 33 | * ダッシュボードオーバーレイは省略しています。 34 | * * 35 | * These codes are licensed under CC0. 36 | * http://creativecommons.org/publicdomain/zero/1.0/deed.ja 37 | */ 38 | 39 | /** uGUIのクリックについて 40 | * 41 | * 使い方 42 | * 1. LaycastRootObjectには、操作したいCanvas(シーン直下に配置)を設定 43 | * 2. Buttonのクリックだけ対応(コントローラーの先端でOverlayを叩くとクリック) 44 | * 3. ButtonのRaycast TargetはONに。ButtonのTextにあるRaycast TargetはOFFに 45 | * 4. CanvasのRender Modeは"Screen Space - Camera"に。 46 | * 5. CanvasのRender Cameraは、RenderTextureを設定したCameraと同じものにすること 47 | * なお、LaycastRootObjectをnull(None)にするとGUI機能は無効化される 48 | */ 49 | 50 | using System.Collections; 51 | using System.Collections.Generic; 52 | using System.Text; 53 | using UnityEngine; 54 | using UnityEngine.EventSystems; 55 | using UnityEngine.UI; 56 | using Valve.VR; //Steam VR 57 | 58 | public class EasyOpenVROverlayForUnity : MonoBehaviour 59 | { 60 | //エラーフラグ 61 | public bool error = true; //初期化失敗 62 | //イベントに関するログを表示するか 63 | public bool eventLog = false; 64 | 65 | [Header("RenderTexture")] 66 | //取得元のRenderTexture 67 | public RenderTexture renderTexture; 68 | 69 | [Header("Transform")] 70 | //Unity準拠の位置と回転 71 | public Vector3 Position = new Vector3(0, -0.5f, 3); 72 | public Vector3 Rotation = new Vector3(0, 0, 0); 73 | public Vector3 Scale = new Vector3(1, 1, 1); 74 | //鏡像反転できるように 75 | public bool MirrorX = false; 76 | public bool MirrorY = false; 77 | 78 | [Header("Setting")] 79 | //オーバーレイの大きさ設定(幅のみ。高さはテクスチャの比から自動計算される) 80 | [Range(0, 100)] 81 | public float width = 5.0f; 82 | 83 | //オーバーレイの透明度を設定 84 | [Range(0, 1)] 85 | public float alpha = 0.2f; 86 | 87 | //表示するか否か 88 | public bool show = true; 89 | 90 | //サイドバイサイド3D 91 | public bool SideBySide = false; 92 | 93 | 94 | [Header("Name")] 95 | //ユーザーが確認するためのオーバーレイの名前 96 | public string OverlayFriendlyName = "SampleOverlay"; 97 | 98 | //グローバルキー(システムのオーバーレイ同士の識別名)。 99 | //ユニークでなければならない。乱数やUUIDなどを勧める 100 | public string OverlayKeyName = "SampleOverlay"; 101 | 102 | [Header("DeviceTracking")] 103 | //絶対空間か 104 | public bool DeviceTracking = true; 105 | 106 | //追従対象デバイス。HMD=0 107 | //public uint DeviceIndex = OpenVR.k_unTrackedDeviceIndex_Hmd; 108 | public TrackingDeviceSelect DeviceIndex = TrackingDeviceSelect.HMD; 109 | private int DeviceIndexOld = (int)TrackingDeviceSelect.None; 110 | 111 | [Header("Absolute space")] 112 | //(絶対空間の場合)ルームスケールか、着座状態か 113 | public bool Seated = false; 114 | 115 | //着座カメラのリセット(リセット後自動でfalseに戻ります) 116 | public bool ResetSeatedCamera = false; 117 | 118 | //追従対象リスト。コントロラーは変動するので特別処理 119 | public enum TrackingDeviceSelect 120 | { 121 | None = -99, 122 | RightController = -2, 123 | LeftController = -1, 124 | HMD = (int)OpenVR.k_unTrackedDeviceIndex_Hmd, 125 | Device1 = 1, 126 | Device2 = 2, 127 | Device3 = 3, 128 | Device4 = 4, 129 | Device5 = 5, 130 | Device6 = 6, 131 | Device7 = 7, 132 | Device8 = 8, 133 | } 134 | 135 | //-------------------------------------------------------------------------- 136 | 137 | [Header("Device Info")] 138 | //現在接続されているデバイス一覧をログに出力(自動でfalseに戻ります) 139 | public bool putLogDevicesInfo = false; 140 | //(デバイスを選択した時点で)現在接続されているデバイス数 141 | public int ConnectedDevices = 0; 142 | //選択デバイス番号 143 | public int SelectedDeviceIndex = 0; 144 | //選択デバイスのシリアル番号 145 | public string DeviceSerialNumber = null; 146 | //選択デバイスのモデル名 147 | public string DeviceRenderModelName = null; 148 | 149 | 150 | [Header("GUI Tap")] 151 | //レイキャスト対象識別用ルートCanvasオブジェクト 152 | public GameObject LaycastRootObject = null; 153 | 154 | //タップ状態管理 155 | public bool tappedLeft = false; 156 | public bool tappedRight = false; 157 | 158 | //タップ距離 159 | public float TapOnDistance = 0.04f; 160 | public float TapOffDistance = 0.043f; 161 | 162 | //カーソル位置表示用変数 163 | public float LeftHandU = -1f; 164 | public float LeftHandV = -1f; 165 | public float LeftHandDistance = -1f; 166 | public float RightHandU = -1f; 167 | public float RightHandV = -1f; 168 | public float RightHandDistance = -1f; 169 | 170 | //右手か左手か 171 | enum LeftOrRight 172 | { 173 | Left = 0, 174 | Right = 1 175 | } 176 | 177 | //-------------------------------------------------------------------------- 178 | 179 | //オーバーレイのハンドル(整数) 180 | private ulong overlayHandle = INVALID_HANDLE; 181 | 182 | //OpenVRシステムインスタンス 183 | private CVRSystem openvr = null; 184 | 185 | //Overlayインスタンス 186 | private CVROverlay overlay = null; 187 | 188 | //オーバーレイに渡すネイティブテクスチャ 189 | private Texture_t overlayTexture; 190 | 191 | //HMD視点位置変換行列 192 | private HmdMatrix34_t p; 193 | 194 | //無効なハンドル 195 | private const ulong INVALID_HANDLE = 0; 196 | 197 | //-------------------------------------------------------------------------- 198 | 199 | //外部から透明度設定切り替え 200 | public void setAlpha(float a) 201 | { 202 | alpha = a; 203 | } 204 | 205 | //外部からdevice切り替え 206 | public void changeToHMD() 207 | { 208 | DeviceIndex = TrackingDeviceSelect.HMD; 209 | } 210 | //外部からdevice切り替え 211 | public void changeToLeftController() 212 | { 213 | DeviceIndex = TrackingDeviceSelect.LeftController; 214 | } 215 | //外部からdevice切り替え 216 | public void changeToRightController() 217 | { 218 | DeviceIndex = TrackingDeviceSelect.RightController; 219 | } 220 | 221 | //-------------------------------------------------------------------------- 222 | 223 | //Overlayが表示されているかどうか外部からcheck 224 | public bool IsVisible() 225 | { 226 | return overlay.IsOverlayVisible(overlayHandle) && !IsError(); 227 | } 228 | 229 | //エラー状態かをチェック 230 | public bool IsError() 231 | { 232 | return error || overlayHandle == INVALID_HANDLE || overlay == null || openvr == null; 233 | } 234 | 235 | //エラー処理(開放処理) 236 | private void ProcessError() 237 | { 238 | 239 | #pragma warning disable 0219 240 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 241 | #pragma warning restore 0219 242 | Debug.Log(Tag + "Begin"); 243 | 244 | //ハンドルを解放 245 | if (overlayHandle != INVALID_HANDLE && overlay != null) 246 | { 247 | overlay.DestroyOverlay(overlayHandle); 248 | } 249 | 250 | overlayHandle = INVALID_HANDLE; 251 | overlay = null; 252 | openvr = null; 253 | error = true; 254 | } 255 | 256 | //オブジェクト破棄時 257 | private void OnDestroy() 258 | { 259 | 260 | #pragma warning disable 0219 261 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 262 | #pragma warning restore 0219 263 | Debug.Log(Tag + "Begin"); 264 | 265 | //ハンドル類の全開放 266 | ProcessError(); 267 | } 268 | 269 | //アプリケーションの終了を検出した時 270 | private void OnApplicationQuit() 271 | { 272 | 273 | #pragma warning disable 0219 274 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 275 | #pragma warning restore 0219 276 | Debug.Log(Tag + "Begin"); 277 | 278 | //ハンドル類の全開放 279 | ProcessError(); 280 | } 281 | 282 | //アプリケーションを終了させる 283 | private void ApplicationQuit() 284 | { 285 | #if UNITY_EDITOR 286 | UnityEditor.EditorApplication.isPlaying = false; 287 | #else 288 | Application.Quit(); 289 | #endif 290 | } 291 | 292 | //-------------------------------------------------------------------------- 293 | 294 | //初期化処理 295 | private void Start() 296 | { 297 | 298 | #pragma warning disable 0219 299 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 300 | #pragma warning restore 0219 301 | Debug.Log(Tag + "Begin"); 302 | 303 | var openVRError = EVRInitError.None; 304 | var overlayError = EVROverlayError.None; 305 | error = false; 306 | 307 | //フレームレートを90fpsにする。(しないと無限に早くなることがある) 308 | Application.targetFrameRate = 90; 309 | Debug.Log(Tag + "Set Frame Rate 90"); 310 | 311 | //OpenVRの初期化 312 | openvr = OpenVR.Init(ref openVRError, EVRApplicationType.VRApplication_Overlay); 313 | if (openVRError != EVRInitError.None) 314 | { 315 | Debug.LogError(Tag + "OpenVRの初期化に失敗." + openVRError.ToString()); 316 | ProcessError(); 317 | return; 318 | } 319 | 320 | //オーバーレイ機能の初期化 321 | overlay = OpenVR.Overlay; 322 | overlayError = overlay.CreateOverlay(OverlayKeyName, OverlayFriendlyName, ref overlayHandle); 323 | if (overlayError != EVROverlayError.None) 324 | { 325 | Debug.LogError(Tag + "Overlayの初期化に失敗. " + overlayError.ToString()); 326 | ProcessError(); 327 | return; 328 | } 329 | 330 | //オーバーレイに渡すテクスチャ種類の設定 331 | var OverlayTextureBounds = new VRTextureBounds_t(); 332 | var isOpenGL = SystemInfo.graphicsDeviceVersion.Contains("OpenGL"); 333 | if (isOpenGL) 334 | { 335 | //pGLuintTexture 336 | overlayTexture.eType = ETextureType.OpenGL; 337 | //上下反転しない 338 | OverlayTextureBounds.uMin = 0; 339 | OverlayTextureBounds.vMin = 0; 340 | OverlayTextureBounds.uMax = 1; 341 | OverlayTextureBounds.vMax = 1; 342 | overlay.SetOverlayTextureBounds(overlayHandle, ref OverlayTextureBounds); 343 | } 344 | else 345 | { 346 | //pTexture 347 | overlayTexture.eType = ETextureType.DirectX; 348 | //上下反転する 349 | OverlayTextureBounds.uMin = 0; 350 | OverlayTextureBounds.vMin = 1; 351 | OverlayTextureBounds.uMax = 1; 352 | OverlayTextureBounds.vMax = 0; 353 | overlay.SetOverlayTextureBounds(overlayHandle, ref OverlayTextureBounds); 354 | } 355 | 356 | //-------- 357 | //showDevices(); 358 | 359 | Debug.Log(Tag + "初期化完了しました"); 360 | } 361 | 362 | private void Update() 363 | { 364 | 365 | #pragma warning disable 0219 366 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 367 | #pragma warning restore 0219 368 | 369 | //エラーが発生した場合や、ハンドルが無効な場合は実行しない 370 | if (IsError()) 371 | { 372 | return; 373 | } 374 | 375 | if (show) 376 | { 377 | //オーバーレイを表示する 378 | overlay.ShowOverlay(overlayHandle); 379 | } 380 | else 381 | { 382 | //オーバーレイを非表示にする 383 | overlay.HideOverlay(overlayHandle); 384 | } 385 | 386 | //イベントを処理する(終了された時true) 387 | if (ProcessEvent()) 388 | { 389 | Debug.Log(Tag + "VRシステムが終了されました"); 390 | ApplicationQuit(); 391 | } 392 | 393 | //オーバーレイが表示されている時 394 | if (overlay.IsOverlayVisible(overlayHandle)) 395 | { 396 | //位置情報と各種設定の更新 397 | updatePosition(); 398 | //表示情報の更新 399 | updateTexture(); 400 | 401 | //Canvasが設定されている場合 402 | if (LaycastRootObject != null) 403 | { 404 | //GUIタッチ機能の処理 405 | updateVRTouch(); 406 | } 407 | } 408 | 409 | if (putLogDevicesInfo) { 410 | showDevices(); 411 | putLogDevicesInfo = false; 412 | } 413 | } 414 | 415 | //位置情報を更新 416 | private void updatePosition() 417 | { 418 | 419 | #pragma warning disable 0219 420 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 421 | #pragma warning restore 0219 422 | 423 | //RenderTextureが生成されているかチェック 424 | if (!renderTexture.IsCreated()) 425 | { 426 | Debug.Log(Tag + "RenderTextureがまだ生成されていない"); 427 | return; 428 | } 429 | 430 | //回転を生成 431 | Quaternion quaternion = Quaternion.Euler(Rotation.x, Rotation.y, Rotation.z); 432 | //座標系を変更(右手系と左手系の入れ替え) 433 | Vector3 position = Position; 434 | position.z = -Position.z; 435 | //HMD視点位置変換行列に書き込む。 436 | Matrix4x4 m = Matrix4x4.TRS(position, quaternion, Scale); 437 | 438 | //鏡像反転 439 | Vector3 Mirroring = new Vector3(MirrorX ? -1 : 1, MirrorY ? -1 : 1, 1); 440 | 441 | //4x4行列を3x4行列に変換する。 442 | p.m0 = Mirroring.x * m.m00; p.m1 = Mirroring.y * m.m01; p.m2 = Mirroring.z * m.m02; p.m3 = m.m03; 443 | p.m4 = Mirroring.x * m.m10; p.m5 = Mirroring.y * m.m11; p.m6 = Mirroring.z * m.m12; p.m7 = m.m13; 444 | p.m8 = Mirroring.x * m.m20; p.m9 = Mirroring.y * m.m21; p.m10 = Mirroring.z * m.m22; p.m11 = m.m23; 445 | 446 | //回転行列を元に相対位置で表示 447 | if (DeviceTracking) 448 | { 449 | //deviceindexを処理(コントローラーなどはその時その時で変わるため) 450 | var idx = OpenVR.k_unTrackedDeviceIndex_Hmd; 451 | switch (DeviceIndex) 452 | { 453 | case TrackingDeviceSelect.LeftController: 454 | idx = openvr.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); 455 | break; 456 | case TrackingDeviceSelect.RightController: 457 | idx = openvr.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); 458 | break; 459 | default: 460 | idx = (uint)DeviceIndex; 461 | break; 462 | } 463 | //device情報に変化があったらInspectorに反映 464 | if (DeviceIndexOld != (int)idx) 465 | { 466 | Debug.Log(Tag + "Device Updated"); 467 | UpdateDeviceInfo(idx); 468 | DeviceIndexOld = (int)idx; 469 | } 470 | 471 | //HMDからの相対的な位置にオーバーレイを表示する。 472 | overlay.SetOverlayTransformTrackedDeviceRelative(overlayHandle, idx, ref p); 473 | } 474 | else 475 | { 476 | //空間の絶対位置にオーバーレイを表示する 477 | if (!Seated) 478 | { 479 | overlay.SetOverlayTransformAbsolute(overlayHandle, ETrackingUniverseOrigin.TrackingUniverseStanding, ref p); 480 | } 481 | else 482 | { 483 | overlay.SetOverlayTransformAbsolute(overlayHandle, ETrackingUniverseOrigin.TrackingUniverseSeated, ref p); 484 | } 485 | } 486 | 487 | if (ResetSeatedCamera) 488 | { 489 | OpenVR.System.ResetSeatedZeroPose(); 490 | ResetSeatedCamera = false; 491 | } 492 | 493 | //オーバーレイの大きさ設定(幅のみ。高さはテクスチャの比から自動計算される) 494 | overlay.SetOverlayWidthInMeters(overlayHandle, width); 495 | 496 | //オーバーレイの透明度を設定 497 | overlay.SetOverlayAlpha(overlayHandle, alpha); 498 | 499 | //マウスカーソルスケールを設定する(これにより表示領域のサイズも決定される) 500 | try 501 | { 502 | HmdVector2_t vecMouseScale = new HmdVector2_t 503 | { 504 | v0 = renderTexture.width, 505 | v1 = renderTexture.height 506 | }; 507 | overlay.SetOverlayMouseScale(overlayHandle, ref vecMouseScale); 508 | } 509 | catch (UnassignedReferenceException e) 510 | { 511 | Debug.LogError(Tag + "RenderTextureがセットされていません " + e.ToString()); 512 | ProcessError(); 513 | return; 514 | } 515 | 516 | } 517 | 518 | //表示情報を更新 519 | private void updateTexture() 520 | { 521 | 522 | #pragma warning disable 0219 523 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 524 | #pragma warning restore 0219 525 | 526 | overlay.SetOverlayFlag(overlayHandle, VROverlayFlags.SideBySide_Parallel, SideBySide); 527 | 528 | //RenderTextureが生成されているかチェック 529 | if (!renderTexture.IsCreated()) 530 | { 531 | Debug.Log(Tag + "RenderTextureがまだ生成されていない"); 532 | return; 533 | } 534 | 535 | //RenderTextureからネイティブテクスチャのハンドルを取得 536 | try 537 | { 538 | overlayTexture.handle = renderTexture.GetNativeTexturePtr(); 539 | } 540 | catch (UnassignedReferenceException e) 541 | { 542 | Debug.LogError(Tag + "RenderTextureがセットされていません " + e.ToString()); 543 | ProcessError(); 544 | return; 545 | } 546 | 547 | //オーバーレイにテクスチャを設定 548 | var overlayError = EVROverlayError.None; 549 | overlayError = overlay.SetOverlayTexture(overlayHandle, ref overlayTexture); 550 | if (overlayError != EVROverlayError.None) 551 | { 552 | Debug.LogError(Tag + "Overlayにテクスチャをセットできませんでした. " + overlayError.ToString()); 553 | //致命的なエラーとしない 554 | return; 555 | } 556 | 557 | } 558 | 559 | //終了イベントをキャッチした時に戻す 560 | private bool ProcessEvent() 561 | { 562 | 563 | #pragma warning disable 0219 564 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 565 | #pragma warning restore 0219 566 | 567 | //イベント構造体のサイズを取得 568 | uint uncbVREvent = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VREvent_t)); 569 | 570 | //イベント情報格納構造体 571 | VREvent_t Event = new VREvent_t(); 572 | //イベントを取り出す 573 | while (overlay.PollNextOverlayEvent(overlayHandle, ref Event, uncbVREvent)) 574 | { 575 | //イベントのログを表示 576 | if (eventLog) 577 | { 578 | Debug.Log(Tag + "Event:" + ((EVREventType)Event.eventType).ToString()); 579 | } 580 | 581 | //イベント情報で分岐 582 | switch ((EVREventType)Event.eventType) 583 | { 584 | case EVREventType.VREvent_Quit: 585 | Debug.Log(Tag + "Quit"); 586 | return true; 587 | } 588 | } 589 | return false; 590 | } 591 | 592 | 593 | //----------おまけ(deviceの詳細情報)------------- 594 | 595 | //全てのdeviceの情報をログに出力する 596 | private void showDevices() 597 | { 598 | #pragma warning disable 0219 599 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 600 | #pragma warning restore 0219 601 | 602 | //すべてのdeviceの接続状態を取得 603 | TrackedDevicePose_t[] allDevicePose = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount]; 604 | openvr.GetDeviceToAbsoluteTrackingPose(ETrackingUniverseOrigin.TrackingUniverseStanding, 0f, allDevicePose); 605 | 606 | //接続されているdeviceの数をカウントする 607 | uint connectedDeviceNum = 0; 608 | for (uint i = 0; i < OpenVR.k_unMaxTrackedDeviceCount; i++) 609 | { 610 | if (allDevicePose[i].bDeviceIsConnected) 611 | { 612 | connectedDeviceNum++; 613 | } 614 | } 615 | 616 | //deviceの詳細情報を1つづつ読み出す 617 | uint connectedDeviceCount = 0; 618 | for (uint i = 0; i < OpenVR.k_unMaxTrackedDeviceCount; i++) 619 | { 620 | //接続中だったら、読み取り完了数を1増やす 621 | if (GetPropertyAndPutLog(i, allDevicePose)) 622 | { 623 | connectedDeviceCount++; 624 | } 625 | //接続されている数だけ読み取り終わったら終了する 626 | if (connectedDeviceCount >= connectedDeviceNum) 627 | { 628 | break; 629 | } 630 | } 631 | } 632 | 633 | //deviceの情報をログに出力する(1項目) 634 | private bool GetPropertyAndPutLog(uint idx, TrackedDevicePose_t[] allDevicePose) 635 | { 636 | #pragma warning disable 0219 637 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 638 | #pragma warning restore 0219 639 | 640 | //接続されているかをチェック 641 | if (allDevicePose[idx].bDeviceIsConnected) 642 | { 643 | //接続されているdevice 644 | 645 | //デバイスシリアル番号(Trackerの識別によく使う)と、deviceモデル名(device種類)を取得 646 | string s1 = GetProperty(idx, ETrackedDeviceProperty.Prop_SerialNumber_String); 647 | string s2 = GetProperty(idx, ETrackedDeviceProperty.Prop_RenderModelName_String); 648 | if (s1 != null && s2 != null) 649 | { 650 | //ログに表示 651 | Debug.Log(Tag + "Device " + idx + ":" + s1 + " : " + s2); 652 | } 653 | else 654 | { 655 | //何らかの理由で取得失敗した 656 | Debug.Log(Tag + "Device " + idx + ": Error"); 657 | } 658 | return true; 659 | } 660 | else 661 | { 662 | //接続されていないdevice 663 | Debug.Log(Tag + "Device " + idx + ": Not connected"); 664 | return false; 665 | } 666 | } 667 | 668 | //指定されたdeviceの情報をInspectorに反映する 669 | private void UpdateDeviceInfo(uint idx) 670 | { 671 | //すべてのdeviceの接続状態を取得 672 | TrackedDevicePose_t[] allDevicePose = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount]; 673 | openvr.GetDeviceToAbsoluteTrackingPose(ETrackingUniverseOrigin.TrackingUniverseStanding, 0f, allDevicePose); 674 | 675 | //接続されているdeviceの数をカウントする 676 | ConnectedDevices = 0; 677 | for (uint i = 0; i < OpenVR.k_unMaxTrackedDeviceCount; i++) 678 | { 679 | if (allDevicePose[i].bDeviceIsConnected) 680 | { 681 | ConnectedDevices++; 682 | } 683 | } 684 | 685 | //deviceの情報をInspectorに反映する 686 | SelectedDeviceIndex = (int)idx; 687 | DeviceSerialNumber = GetProperty(idx, ETrackedDeviceProperty.Prop_SerialNumber_String); 688 | DeviceRenderModelName = GetProperty(idx, ETrackedDeviceProperty.Prop_RenderModelName_String); 689 | } 690 | 691 | //device情報を取得する 692 | private string GetProperty(uint idx, ETrackedDeviceProperty prop) 693 | { 694 | 695 | #pragma warning disable 0219 696 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 697 | #pragma warning restore 0219 698 | 699 | ETrackedPropertyError error = new ETrackedPropertyError(); 700 | //device情報を取得するのに必要な文字数を取得 701 | uint size = openvr.GetStringTrackedDeviceProperty(idx, prop, null, 0, ref error); 702 | if (error != ETrackedPropertyError.TrackedProp_BufferTooSmall) 703 | { 704 | return null; 705 | } 706 | 707 | StringBuilder s = new StringBuilder(); 708 | s.Length = (int)size; //文字長さ確保 709 | //device情報を取得する 710 | openvr.GetStringTrackedDeviceProperty(idx, prop, s, size, ref error); 711 | if (error != ETrackedPropertyError.TrackedProp_Success) 712 | { 713 | return null; 714 | } 715 | return s.ToString(); 716 | } 717 | 718 | 719 | //----------おまけ(コントローラーでOverlayを叩いてuGUIをクリックできるやつ)------------- 720 | 721 | //uGUIクリックを実現する 722 | private void updateVRTouch() 723 | { 724 | 725 | #pragma warning disable 0219 726 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 727 | #pragma warning restore 0219 728 | 729 | //コントローラのindex 730 | 731 | //VR接続されているすべてのデバイスの情報を取得 732 | TrackedDevicePose_t[] allDevicePose = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount]; 733 | openvr.GetDeviceToAbsoluteTrackingPose(ETrackingUniverseOrigin.TrackingUniverseStanding, 0f, allDevicePose); 734 | 735 | //Overlayのレイ走査結果を格納する変数 736 | VROverlayIntersectionResults_t results = new VROverlayIntersectionResults_t(); 737 | 738 | /* 739 | //視線による操作 740 | uint Hmdidx = OpenVR.k_unTrackedDeviceIndex_Hmd; 741 | if (checkRay(Hmdidx, allDevicePose, ref results)) 742 | { 743 | parent.setCursorPosition(results, LeftOrRight.Right, channel); 744 | Debug.Log(DEBUG_TAG + "HMD u:"+results.vUVs.v0+" v:"+ results.vUVs.v1+" d:"+results.fDistance); 745 | } 746 | */ 747 | //左手コントローラーの情報取得 748 | uint Leftidx = openvr.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); 749 | if (checkRay(Leftidx, allDevicePose, ref results)) 750 | { 751 | //線上にオーバーレイがある場合は続けて処理 752 | CheckTapping(results, LeftOrRight.Left, ref tappedLeft); 753 | 754 | //カーソル表示用に更新 755 | LeftHandU = results.vUVs.v0 * renderTexture.width; 756 | LeftHandV = renderTexture.height - results.vUVs.v1 * renderTexture.height; 757 | LeftHandDistance = results.fDistance; 758 | } 759 | else 760 | { 761 | LeftHandU = -1f; 762 | LeftHandV = -1f; 763 | LeftHandDistance = -1f; 764 | } 765 | //右手コントローラーの情報取得 766 | uint Rightidx = openvr.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); 767 | if (checkRay(Rightidx, allDevicePose, ref results)) 768 | { 769 | //線上にオーバーレイがある場合は続けて処理 770 | CheckTapping(results, LeftOrRight.Right, ref tappedRight); 771 | 772 | //カーソル表示用に更新 773 | RightHandU = results.vUVs.v0 * renderTexture.width; 774 | RightHandV = renderTexture.height - results.vUVs.v1 * renderTexture.height; 775 | RightHandDistance = results.fDistance; 776 | } 777 | else 778 | { 779 | RightHandU = -1f; 780 | RightHandV = -1f; 781 | RightHandDistance = -1f; 782 | } 783 | 784 | } 785 | 786 | //指定されたdeviceが有効かチェックした上で、オーバーレイと交点を持つかチェック 787 | private bool checkRay(uint idx, TrackedDevicePose_t[] allDevicePose, ref VROverlayIntersectionResults_t results) 788 | { 789 | 790 | #pragma warning disable 0219 791 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 792 | #pragma warning restore 0219 793 | 794 | //device indexが有効 795 | if (idx != OpenVR.k_unTrackedDeviceIndexInvalid) 796 | { 797 | //接続されていて姿勢情報が有効 798 | if (allDevicePose[idx].bDeviceIsConnected && allDevicePose[idx].bPoseIsValid) 799 | { 800 | //姿勢情報などを変換してもらう 801 | TrackedDevicePose_t Pose = allDevicePose[idx]; 802 | SteamVR_Utils.RigidTransform Trans = new SteamVR_Utils.RigidTransform(Pose.mDeviceToAbsoluteTracking); 803 | 804 | //コントローラー用に45度前方に傾けた方向ベクトルを計算 805 | Vector3 vect = (Trans.rot * Quaternion.AngleAxis(45, Vector3.right)) * Vector3.forward; 806 | 807 | return ComputeOverlayIntersection(Trans.pos, vect, ref results); 808 | } 809 | } 810 | return false; 811 | } 812 | 813 | //オーバーレイと交点を持つかチェック 814 | private bool ComputeOverlayIntersection(Vector3 pos, Vector3 rotvect, ref VROverlayIntersectionResults_t results) 815 | { 816 | 817 | #pragma warning disable 0219 818 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 819 | #pragma warning restore 0219 820 | 821 | //レイ照射情報 822 | VROverlayIntersectionParams_t param = new VROverlayIntersectionParams_t(); 823 | //レイ発射元位置 824 | param.vSource = new HmdVector3_t 825 | { 826 | v0 = pos.x, 827 | v1 = pos.y, 828 | v2 = -pos.z //右手系 to 左手系 829 | }; 830 | //レイ発射単位方向ベクトル 831 | param.vDirection = new HmdVector3_t 832 | { 833 | v0 = rotvect.x, 834 | v1 = rotvect.y, 835 | v2 = -rotvect.z //右手系 to 左手系 836 | }; 837 | //ルーム空間座標系で照射 838 | param.eOrigin = ETrackingUniverseOrigin.TrackingUniverseStanding; 839 | 840 | //Overlayと交差していればtrue、していなければfalseで、詳細情報がresultsに入る 841 | return overlay.ComputeOverlayIntersection(overlayHandle, ref param, ref results); 842 | } 843 | 844 | //タップされているかどうかを調べる 845 | private void CheckTapping(VROverlayIntersectionResults_t results, LeftOrRight lr, ref bool tapped) 846 | { 847 | 848 | #pragma warning disable 0219 849 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 850 | #pragma warning restore 0219 851 | 852 | //コントローラとオーバーレイの距離が一定以下なら 853 | if (results.fDistance < TapOnDistance && !tapped) 854 | { 855 | //タップされた 856 | tapped = true; 857 | haptic(lr); 858 | 859 | //クリック処理 860 | uGUIclick(results); 861 | } 862 | //コントローラとオーバーレイの距離が一定以上なら 863 | if (results.fDistance > TapOffDistance && tapped) 864 | { 865 | //離れた 866 | tapped = false; 867 | haptic(lr); 868 | } 869 | } 870 | 871 | //振動フィードバックを行う 872 | private void haptic(LeftOrRight lr) 873 | { 874 | 875 | #pragma warning disable 0219 876 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 877 | #pragma warning restore 0219 878 | 879 | //左手コントローラーが有効かチェック 880 | uint Leftidx = openvr.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); 881 | if (Leftidx != OpenVR.k_unTrackedDeviceIndexInvalid && lr == LeftOrRight.Left) 882 | { 883 | //ぶるっと 884 | openvr.TriggerHapticPulse(Leftidx, 0, 3000); 885 | } 886 | //右手コントローラーが有効かチェック 887 | uint Rightidx = openvr.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); 888 | if (Rightidx != OpenVR.k_unTrackedDeviceIndexInvalid && lr == LeftOrRight.Right) 889 | { 890 | //ぶるっと 891 | openvr.TriggerHapticPulse(Rightidx, 0, 3000); 892 | } 893 | } 894 | 895 | //Canvas上の要素を特定してクリックする 896 | private void uGUIclick(VROverlayIntersectionResults_t results) 897 | { 898 | #pragma warning disable 0219 899 | string Tag = "[" + this.GetType().Name + ":" + System.Reflection.MethodBase.GetCurrentMethod(); //クラス名とメソッド名を自動取得 900 | #pragma warning restore 0219 901 | 902 | //クリック用uv座標を計算 903 | float u = results.vUVs.v0 * renderTexture.width; 904 | float v = renderTexture.height - results.vUVs.v1 * renderTexture.height; 905 | 906 | //Canvas上のレイキャストのために座標をセット 907 | Vector2 ScreenPoint = new Vector2(u, v); 908 | PointerEventData pointer = new PointerEventData(EventSystem.current) 909 | { 910 | position = ScreenPoint 911 | }; 912 | 913 | //レイキャスト結果格納用リストを確保 914 | List result = new List(); 915 | 916 | //RaycastAllはレイキャスターを叩く。 917 | //CanvasについていたりCameraについていたりするすべてのレイキャスターを叩く(要らないものは切っておくとよい) 918 | EventSystem.current.RaycastAll(pointer, result); 919 | 920 | //検出した要素の数と座標 921 | 922 | Debug.Log(Tag + "count:" + result.Count + " u:" + u + " / v:" + v); 923 | 924 | //一番最初に見つけた要素にクリック処理を行う 925 | for (int i = 0; i < result.Count; i++) 926 | { 927 | var res = result[i]; 928 | 929 | //一番最初に引っ掛けたものを叩く(target以外はcheckを外しておく) 930 | if (res.isValid) 931 | { 932 | Debug.Log(Tag + res.gameObject.name + " at " + res.gameObject.transform.root.name); 933 | //対象にしたいルートオブジェクトの子かを調べる 934 | if (res.gameObject.transform.root.name == LaycastRootObject.name) 935 | { 936 | ExecuteEvents.Execute(res.gameObject, pointer, ExecuteEvents.pointerClickHandler); 937 | break; 938 | } 939 | } 940 | } 941 | } 942 | 943 | 944 | } --------------------------------------------------------------------------------