SteamVR_Render.cs 11 KB

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