SteamVR_Render.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Handles rendering of all SteamVR_Cameras
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using System.Collections;
  8. using Valve.VR;
  9. namespace Valve.VR
  10. {
  11. public class SteamVR_Render : MonoBehaviour
  12. {
  13. public SteamVR_ExternalCamera externalCamera;
  14. public string externalCameraConfigPath = "externalcamera.cfg";
  15. public static EVREye eye { get; private set; }
  16. public static SteamVR_Render instance { get { return SteamVR_Behaviour.instance.steamvr_render; } }
  17. static private bool isQuitting;
  18. void OnApplicationQuit()
  19. {
  20. isQuitting = true;
  21. SteamVR.SafeDispose();
  22. }
  23. static public void Add(SteamVR_Camera vrcam)
  24. {
  25. if (!isQuitting)
  26. instance.AddInternal(vrcam);
  27. }
  28. static public void Remove(SteamVR_Camera vrcam)
  29. {
  30. if (!isQuitting && instance != null)
  31. instance.RemoveInternal(vrcam);
  32. }
  33. static public SteamVR_Camera Top()
  34. {
  35. if (!isQuitting)
  36. return instance.TopInternal();
  37. return null;
  38. }
  39. private SteamVR_Camera[] cameras = new SteamVR_Camera[0];
  40. void AddInternal(SteamVR_Camera vrcam)
  41. {
  42. var camera = vrcam.GetComponent<Camera>();
  43. var length = cameras.Length;
  44. var sorted = new SteamVR_Camera[length + 1];
  45. int insert = 0;
  46. for (int i = 0; i < length; i++)
  47. {
  48. var c = cameras[i].GetComponent<Camera>();
  49. if (i == insert && c.depth > camera.depth)
  50. sorted[insert++] = vrcam;
  51. sorted[insert++] = cameras[i];
  52. }
  53. if (insert == length)
  54. sorted[insert] = vrcam;
  55. cameras = sorted;
  56. }
  57. void RemoveInternal(SteamVR_Camera vrcam)
  58. {
  59. var length = cameras.Length;
  60. int count = 0;
  61. for (int i = 0; i < length; i++)
  62. {
  63. var c = cameras[i];
  64. if (c == vrcam)
  65. ++count;
  66. }
  67. if (count == 0)
  68. return;
  69. var sorted = new SteamVR_Camera[length - count];
  70. int insert = 0;
  71. for (int i = 0; i < length; i++)
  72. {
  73. var c = cameras[i];
  74. if (c != vrcam)
  75. sorted[insert++] = c;
  76. }
  77. cameras = sorted;
  78. }
  79. SteamVR_Camera TopInternal()
  80. {
  81. if (cameras.Length > 0)
  82. return cameras[cameras.Length - 1];
  83. return null;
  84. }
  85. public TrackedDevicePose_t[] poses = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount];
  86. public TrackedDevicePose_t[] gamePoses = new TrackedDevicePose_t[0];
  87. static private bool _pauseRendering;
  88. static public bool pauseRendering
  89. {
  90. get { return _pauseRendering; }
  91. set
  92. {
  93. _pauseRendering = value;
  94. var compositor = OpenVR.Compositor;
  95. if (compositor != null)
  96. compositor.SuspendRendering(value);
  97. }
  98. }
  99. private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
  100. private IEnumerator RenderLoop()
  101. {
  102. while (Application.isPlaying)
  103. {
  104. yield return waitForEndOfFrame;
  105. if (pauseRendering)
  106. continue;
  107. var compositor = OpenVR.Compositor;
  108. if (compositor != null)
  109. {
  110. if (!compositor.CanRenderScene())
  111. continue;
  112. compositor.SetTrackingSpace(SteamVR.settings.trackingSpace);
  113. }
  114. var overlay = SteamVR_Overlay.instance;
  115. if (overlay != null)
  116. overlay.UpdateOverlay();
  117. if (CheckExternalCamera())
  118. RenderExternalCamera();
  119. }
  120. }
  121. private bool? doesPathExist = null;
  122. private bool CheckExternalCamera()
  123. {
  124. if (doesPathExist == false)
  125. return false;
  126. else if (doesPathExist == null)
  127. doesPathExist = System.IO.File.Exists(externalCameraConfigPath);
  128. if (externalCamera == null && doesPathExist == true)
  129. {
  130. GameObject prefab = Resources.Load<GameObject>("SteamVR_ExternalCamera");
  131. if (prefab == null)
  132. {
  133. doesPathExist = false;
  134. return false;
  135. }
  136. else
  137. {
  138. if (SteamVR_Settings.instance.legacyMixedRealityCamera)
  139. {
  140. if (SteamVR_ExternalCamera_LegacyManager.hasCamera == false)
  141. return false;
  142. GameObject instance = Instantiate(prefab);
  143. instance.gameObject.name = "External Camera";
  144. externalCamera = instance.transform.GetChild(0).GetComponent<SteamVR_ExternalCamera>();
  145. externalCamera.configPath = externalCameraConfigPath;
  146. externalCamera.ReadConfig();
  147. externalCamera.SetupDeviceIndex(SteamVR_ExternalCamera_LegacyManager.cameraIndex);
  148. }
  149. else
  150. {
  151. SteamVR_Action_Pose cameraPose = SteamVR_Settings.instance.mixedRealityCameraPose;
  152. SteamVR_Input_Sources cameraSource = SteamVR_Settings.instance.mixedRealityCameraInputSource;
  153. if (cameraPose != null && SteamVR_Settings.instance.mixedRealityActionSetAutoEnable)
  154. {
  155. if (cameraPose.actionSet != null && cameraPose.actionSet.IsActive(cameraSource) == false)
  156. cameraPose.actionSet.Activate(cameraSource);
  157. }
  158. if (cameraPose == null)
  159. {
  160. doesPathExist = false;
  161. return false;
  162. }
  163. if (cameraPose != null && cameraPose[cameraSource].active && cameraPose[cameraSource].deviceIsConnected)
  164. {
  165. GameObject instance = Instantiate(prefab);
  166. instance.gameObject.name = "External Camera";
  167. externalCamera = instance.transform.GetChild(0).GetComponent<SteamVR_ExternalCamera>();
  168. externalCamera.configPath = externalCameraConfigPath;
  169. externalCamera.ReadConfig();
  170. externalCamera.SetupPose(cameraPose, cameraSource);
  171. }
  172. }
  173. }
  174. }
  175. return (externalCamera != null);
  176. }
  177. void RenderExternalCamera()
  178. {
  179. if (externalCamera == null)
  180. return;
  181. if (!externalCamera.gameObject.activeInHierarchy)
  182. return;
  183. var frameSkip = (int)Mathf.Max(externalCamera.config.frameSkip, 0.0f);
  184. if (Time.frameCount % (frameSkip + 1) != 0)
  185. return;
  186. // Keep external camera relative to the most relevant vr camera.
  187. externalCamera.AttachToCamera(TopInternal());
  188. externalCamera.RenderNear();
  189. externalCamera.RenderFar();
  190. }
  191. float sceneResolutionScale = 1.0f, timeScale = 1.0f;
  192. private void OnInputFocus(bool hasFocus)
  193. {
  194. if (SteamVR.active == false)
  195. return;
  196. if (hasFocus)
  197. {
  198. if (SteamVR.settings.pauseGameWhenDashboardVisible)
  199. {
  200. Time.timeScale = timeScale;
  201. }
  202. SteamVR_Camera.sceneResolutionScale = sceneResolutionScale;
  203. }
  204. else
  205. {
  206. if (SteamVR.settings.pauseGameWhenDashboardVisible)
  207. {
  208. timeScale = Time.timeScale;
  209. Time.timeScale = 0.0f;
  210. }
  211. sceneResolutionScale = SteamVR_Camera.sceneResolutionScale;
  212. SteamVR_Camera.sceneResolutionScale = 0.5f;
  213. }
  214. }
  215. private string GetScreenshotFilename(uint screenshotHandle, EVRScreenshotPropertyFilenames screenshotPropertyFilename)
  216. {
  217. var error = EVRScreenshotError.None;
  218. var capacity = OpenVR.Screenshots.GetScreenshotPropertyFilename(screenshotHandle, screenshotPropertyFilename, null, 0, ref error);
  219. if (error != EVRScreenshotError.None && error != EVRScreenshotError.BufferTooSmall)
  220. return null;
  221. if (capacity > 1)
  222. {
  223. var result = new System.Text.StringBuilder((int)capacity);
  224. OpenVR.Screenshots.GetScreenshotPropertyFilename(screenshotHandle, screenshotPropertyFilename, result, capacity, ref error);
  225. if (error != EVRScreenshotError.None)
  226. return null;
  227. return result.ToString();
  228. }
  229. return null;
  230. }
  231. private void OnRequestScreenshot(VREvent_t vrEvent)
  232. {
  233. var screenshotHandle = vrEvent.data.screenshot.handle;
  234. var screenshotType = (EVRScreenshotType)vrEvent.data.screenshot.type;
  235. if (screenshotType == EVRScreenshotType.StereoPanorama)
  236. {
  237. string previewFilename = GetScreenshotFilename(screenshotHandle, EVRScreenshotPropertyFilenames.Preview);
  238. string VRFilename = GetScreenshotFilename(screenshotHandle, EVRScreenshotPropertyFilenames.VR);
  239. if (previewFilename == null || VRFilename == null)
  240. return;
  241. // Do the stereo panorama screenshot
  242. // Figure out where the view is
  243. GameObject screenshotPosition = new GameObject("screenshotPosition");
  244. screenshotPosition.transform.position = SteamVR_Render.Top().transform.position;
  245. screenshotPosition.transform.rotation = SteamVR_Render.Top().transform.rotation;
  246. screenshotPosition.transform.localScale = SteamVR_Render.Top().transform.lossyScale;
  247. SteamVR_Utils.TakeStereoScreenshot(screenshotHandle, screenshotPosition, 32, 0.064f, ref previewFilename, ref VRFilename);
  248. // and submit it
  249. OpenVR.Screenshots.SubmitScreenshot(screenshotHandle, screenshotType, previewFilename, VRFilename);
  250. }
  251. }
  252. private EVRScreenshotType[] screenshotTypes = new EVRScreenshotType[] { EVRScreenshotType.StereoPanorama };
  253. private void OnEnable()
  254. {
  255. StartCoroutine(RenderLoop());
  256. SteamVR_Events.InputFocus.Listen(OnInputFocus);
  257. SteamVR_Events.System(EVREventType.VREvent_RequestScreenshot).Listen(OnRequestScreenshot);
  258. if (SteamVR_Settings.instance.legacyMixedRealityCamera)
  259. SteamVR_ExternalCamera_LegacyManager.SubscribeToNewPoses();
  260. #if UNITY_2017_1_OR_NEWER
  261. Application.onBeforeRender += OnBeforeRender;
  262. #else
  263. Camera.onPreCull += OnCameraPreCull;
  264. #endif
  265. if (SteamVR.initializedState == SteamVR.InitializedStates.InitializeSuccess)
  266. OpenVR.Screenshots.HookScreenshot(screenshotTypes);
  267. else
  268. SteamVR_Events.Initialized.AddListener(OnSteamVRInitialized);
  269. }
  270. private void OnSteamVRInitialized(bool success)
  271. {
  272. if (success)
  273. OpenVR.Screenshots.HookScreenshot(screenshotTypes);
  274. }
  275. private void OnDisable()
  276. {
  277. StopAllCoroutines();
  278. SteamVR_Events.InputFocus.Remove(OnInputFocus);
  279. SteamVR_Events.System(EVREventType.VREvent_RequestScreenshot).Remove(OnRequestScreenshot);
  280. #if UNITY_2017_1_OR_NEWER
  281. Application.onBeforeRender -= OnBeforeRender;
  282. #else
  283. Camera.onPreCull -= OnCameraPreCull;
  284. #endif
  285. if (SteamVR.initializedState != SteamVR.InitializedStates.InitializeSuccess)
  286. SteamVR_Events.Initialized.RemoveListener(OnSteamVRInitialized);
  287. }
  288. public void UpdatePoses()
  289. {
  290. var compositor = OpenVR.Compositor;
  291. if (compositor != null)
  292. {
  293. compositor.GetLastPoses(poses, gamePoses);
  294. SteamVR_Events.NewPoses.Send(poses);
  295. SteamVR_Events.NewPosesApplied.Send();
  296. }
  297. }
  298. #if UNITY_2017_1_OR_NEWER
  299. void OnBeforeRender()
  300. {
  301. if (SteamVR.active == false)
  302. return;
  303. if (SteamVR.settings.IsPoseUpdateMode(SteamVR_UpdateModes.OnPreCull))
  304. {
  305. UpdatePoses();
  306. }
  307. }
  308. #else
  309. void OnCameraPreCull(Camera cam)
  310. {
  311. if (SteamVR.active == false)
  312. return;
  313. #if UNITY_2017_1_OR_NEWER
  314. if (cam.cameraType != CameraType.VR)
  315. return;
  316. #else
  317. //custom code
  318. if (!cam.stereoEnabled) //if not main camera (stereoEnabled isn't perfect, but it is the fast/easiest way to check this in Unity 5.4)
  319. {
  320. return;
  321. }
  322. #endif
  323. // Only update poses on the first camera per frame.
  324. if (Time.frameCount != lastFrameCount)
  325. {
  326. lastFrameCount = Time.frameCount;
  327. if (SteamVR.settings.IsPoseUpdateMode(SteamVR_UpdateModes.OnPreCull))
  328. {
  329. UpdatePoses();
  330. }
  331. }
  332. }
  333. static int lastFrameCount = -1;
  334. #endif
  335. void Update()
  336. {
  337. if (SteamVR.active == false)
  338. return;
  339. // Dispatch any OpenVR events.
  340. var system = OpenVR.System;
  341. if (system == null)
  342. return;
  343. UpdatePoses();
  344. var vrEvent = new VREvent_t();
  345. var size = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VREvent_t));
  346. for (int i = 0; i < 64; i++)
  347. {
  348. if (!system.PollNextEvent(ref vrEvent, size))
  349. break;
  350. switch ((EVREventType)vrEvent.eventType)
  351. {
  352. case EVREventType.VREvent_InputFocusCaptured: // another app has taken focus (likely dashboard)
  353. if (vrEvent.data.process.oldPid == 0)
  354. {
  355. SteamVR_Events.InputFocus.Send(false);
  356. }
  357. break;
  358. case EVREventType.VREvent_InputFocusReleased: // that app has released input focus
  359. if (vrEvent.data.process.pid == 0)
  360. {
  361. SteamVR_Events.InputFocus.Send(true);
  362. }
  363. break;
  364. case EVREventType.VREvent_ShowRenderModels:
  365. SteamVR_Events.HideRenderModels.Send(false);
  366. break;
  367. case EVREventType.VREvent_HideRenderModels:
  368. SteamVR_Events.HideRenderModels.Send(true);
  369. break;
  370. default:
  371. SteamVR_Events.System((EVREventType)vrEvent.eventType).Send(vrEvent);
  372. break;
  373. }
  374. }
  375. // Ensure various settings to minimize latency.
  376. Application.targetFrameRate = -1;
  377. Application.runInBackground = true; // don't require companion window focus
  378. QualitySettings.maxQueuedFrames = -1;
  379. QualitySettings.vSyncCount = 0; // this applies to the companion window
  380. if (SteamVR.settings.lockPhysicsUpdateRateToRenderFrequency && Time.timeScale > 0.0f)
  381. {
  382. var vr = SteamVR.instance;
  383. if (vr != null && Application.isPlaying)
  384. {
  385. //var timing = new Compositor_FrameTiming();
  386. //timing.m_nSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(Compositor_FrameTiming));
  387. //vr.compositor.GetFrameTiming(ref timing, 0);
  388. Time.fixedDeltaTime = Time.timeScale / vr.hmd_DisplayFrequency;
  389. }
  390. }
  391. }
  392. }
  393. }