SteamVR_ExternalCamera.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Used to render an external camera of vr player (split front/back).
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using UnityEngine.Rendering;
  8. using Valve.VR;
  9. namespace Valve.VR
  10. {
  11. public class SteamVR_ExternalCamera : MonoBehaviour
  12. {
  13. [System.Serializable]
  14. public struct Config
  15. {
  16. public float x, y, z;
  17. public float rx, ry, rz;
  18. public float fov;
  19. public float near, far;
  20. public float sceneResolutionScale;
  21. public float frameSkip;
  22. public float nearOffset, farOffset;
  23. public float hmdOffset;
  24. public float r, g, b, a; // chroma key override
  25. public bool disableStandardAssets;
  26. }
  27. public Config config;
  28. public string configPath;
  29. [Tooltip("This will automatically activate the action set the specified pose belongs to. And deactivate it when this component is disabled.")]
  30. public bool autoEnableDisableActionSet = true;
  31. public void ReadConfig()
  32. {
  33. try
  34. {
  35. var mCam = new HmdMatrix34_t();
  36. var readCamMatrix = false;
  37. object c = config; // box
  38. var lines = System.IO.File.ReadAllLines(configPath);
  39. foreach (var line in lines)
  40. {
  41. var split = line.Split('=');
  42. if (split.Length == 2)
  43. {
  44. var key = split[0];
  45. if (key == "m")
  46. {
  47. var values = split[1].Split(',');
  48. if (values.Length == 12)
  49. {
  50. mCam.m0 = float.Parse(values[0]);
  51. mCam.m1 = float.Parse(values[1]);
  52. mCam.m2 = float.Parse(values[2]);
  53. mCam.m3 = float.Parse(values[3]);
  54. mCam.m4 = float.Parse(values[4]);
  55. mCam.m5 = float.Parse(values[5]);
  56. mCam.m6 = float.Parse(values[6]);
  57. mCam.m7 = float.Parse(values[7]);
  58. mCam.m8 = float.Parse(values[8]);
  59. mCam.m9 = float.Parse(values[9]);
  60. mCam.m10 = float.Parse(values[10]);
  61. mCam.m11 = float.Parse(values[11]);
  62. readCamMatrix = true;
  63. }
  64. }
  65. #if !UNITY_METRO
  66. else if (key == "disableStandardAssets")
  67. {
  68. var field = c.GetType().GetField(key);
  69. if (field != null)
  70. field.SetValue(c, bool.Parse(split[1]));
  71. }
  72. else
  73. {
  74. var field = c.GetType().GetField(key);
  75. if (field != null)
  76. field.SetValue(c, float.Parse(split[1]));
  77. }
  78. #endif
  79. }
  80. }
  81. config = (Config)c; //unbox
  82. // Convert calibrated camera matrix settings.
  83. if (readCamMatrix)
  84. {
  85. var t = new SteamVR_Utils.RigidTransform(mCam);
  86. config.x = t.pos.x;
  87. config.y = t.pos.y;
  88. config.z = t.pos.z;
  89. var angles = t.rot.eulerAngles;
  90. config.rx = angles.x;
  91. config.ry = angles.y;
  92. config.rz = angles.z;
  93. }
  94. }
  95. catch { }
  96. // Clear target so AttachToCamera gets called to pick up any changes.
  97. target = null;
  98. #if !UNITY_METRO
  99. // Listen for changes.
  100. if (watcher == null)
  101. {
  102. var fi = new System.IO.FileInfo(configPath);
  103. watcher = new System.IO.FileSystemWatcher(fi.DirectoryName, fi.Name);
  104. watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite;
  105. watcher.Changed += new System.IO.FileSystemEventHandler(OnChanged);
  106. watcher.EnableRaisingEvents = true;
  107. }
  108. }
  109. void OnChanged(object source, System.IO.FileSystemEventArgs e)
  110. {
  111. ReadConfig();
  112. }
  113. System.IO.FileSystemWatcher watcher;
  114. #else
  115. }
  116. #endif
  117. Camera cam;
  118. Transform target;
  119. GameObject clipQuad;
  120. Material clipMaterial;
  121. protected SteamVR_ActionSet activatedActionSet;
  122. protected SteamVR_Input_Sources activatedInputSource;
  123. public void AttachToCamera(SteamVR_Camera steamVR_Camera)
  124. {
  125. Camera vrcam;
  126. if (steamVR_Camera == null)
  127. {
  128. vrcam = Camera.main;
  129. if (target == vrcam.transform)
  130. return;
  131. target = vrcam.transform;
  132. }
  133. else
  134. {
  135. vrcam = steamVR_Camera.camera;
  136. if (target == steamVR_Camera.head)
  137. return;
  138. target = steamVR_Camera.head;
  139. }
  140. var root = transform.parent;
  141. var origin = target.parent;
  142. root.parent = origin;
  143. root.localPosition = Vector3.zero;
  144. root.localRotation = Quaternion.identity;
  145. root.localScale = Vector3.one;
  146. // Make a copy of the eye camera to pick up any camera fx.
  147. vrcam.enabled = false;
  148. var go = Instantiate(vrcam.gameObject);
  149. vrcam.enabled = true;
  150. go.name = "camera";
  151. DestroyImmediate(go.GetComponent<SteamVR_Camera>());
  152. DestroyImmediate(go.GetComponent<SteamVR_Fade>());
  153. cam = go.GetComponent<Camera>();
  154. cam.stereoTargetEye = StereoTargetEyeMask.None;
  155. cam.fieldOfView = config.fov;
  156. cam.useOcclusionCulling = false;
  157. cam.enabled = false; // manually rendered
  158. cam.rect = new Rect(0, 0, 1, 1); //fix order of operations issue
  159. colorMat = new Material(Shader.Find("Custom/SteamVR_ColorOut"));
  160. alphaMat = new Material(Shader.Find("Custom/SteamVR_AlphaOut"));
  161. clipMaterial = new Material(Shader.Find("Custom/SteamVR_ClearAll"));
  162. var offset = go.transform;
  163. offset.parent = transform;
  164. offset.localPosition = new Vector3(config.x, config.y, config.z);
  165. offset.localRotation = Quaternion.Euler(config.rx, config.ry, config.rz);
  166. offset.localScale = Vector3.one;
  167. // Strip children of cloned object (AudioListener in particular).
  168. while (offset.childCount > 0)
  169. DestroyImmediate(offset.GetChild(0).gameObject);
  170. // Setup clipping quad (using camera clip causes problems with shadows).
  171. clipQuad = GameObject.CreatePrimitive(PrimitiveType.Quad);
  172. clipQuad.name = "ClipQuad";
  173. DestroyImmediate(clipQuad.GetComponent<MeshCollider>());
  174. var clipRenderer = clipQuad.GetComponent<MeshRenderer>();
  175. clipRenderer.material = clipMaterial;
  176. clipRenderer.shadowCastingMode = ShadowCastingMode.Off;
  177. clipRenderer.receiveShadows = false;
  178. clipRenderer.lightProbeUsage = LightProbeUsage.Off;
  179. clipRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
  180. var clipTransform = clipQuad.transform;
  181. clipTransform.parent = offset;
  182. clipTransform.localScale = new Vector3(1000.0f, 1000.0f, 1.0f);
  183. clipTransform.localRotation = Quaternion.identity;
  184. clipQuad.SetActive(false);
  185. }
  186. public float GetTargetDistance()
  187. {
  188. if (target == null)
  189. return config.near + 0.01f;
  190. var offset = cam.transform;
  191. var forward = new Vector3(offset.forward.x, 0.0f, offset.forward.z).normalized;
  192. var targetPos = target.position + new Vector3(target.forward.x, 0.0f, target.forward.z).normalized * config.hmdOffset;
  193. var distance = -(new Plane(forward, targetPos)).GetDistanceToPoint(offset.position);
  194. return Mathf.Clamp(distance, config.near + 0.01f, config.far - 0.01f);
  195. }
  196. Material colorMat, alphaMat;
  197. public void RenderNear()
  198. {
  199. var w = Screen.width / 2;
  200. var h = Screen.height / 2;
  201. if (cam.targetTexture == null || cam.targetTexture.width != w || cam.targetTexture.height != h)
  202. {
  203. var tex = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32);
  204. tex.antiAliasing = QualitySettings.antiAliasing == 0 ? 1 : QualitySettings.antiAliasing;
  205. cam.targetTexture = tex;
  206. }
  207. cam.nearClipPlane = config.near;
  208. cam.farClipPlane = config.far;
  209. var clearFlags = cam.clearFlags;
  210. var backgroundColor = cam.backgroundColor;
  211. cam.clearFlags = CameraClearFlags.Color;
  212. cam.backgroundColor = Color.clear;
  213. clipMaterial.color = new Color(config.r, config.g, config.b, config.a);
  214. float dist = Mathf.Clamp(GetTargetDistance() + config.nearOffset, config.near, config.far);
  215. var clipParent = clipQuad.transform.parent;
  216. clipQuad.transform.position = clipParent.position + clipParent.forward * dist;
  217. MonoBehaviour[] behaviours = null;
  218. bool[] wasEnabled = null;
  219. if (config.disableStandardAssets)
  220. {
  221. behaviours = cam.gameObject.GetComponents<MonoBehaviour>();
  222. wasEnabled = new bool[behaviours.Length];
  223. for (int i = 0; i < behaviours.Length; i++)
  224. {
  225. var behaviour = behaviours[i];
  226. if (behaviour.enabled && behaviour.GetType().ToString().StartsWith("UnityStandardAssets."))
  227. {
  228. behaviour.enabled = false;
  229. wasEnabled[i] = true;
  230. }
  231. }
  232. }
  233. clipQuad.SetActive(true);
  234. cam.Render();
  235. Graphics.DrawTexture(new Rect(0, 0, w, h), cam.targetTexture, colorMat);
  236. // Re-render scene with post-processing fx disabled (if necessary) since they override alpha.
  237. var pp = cam.gameObject.GetComponent("PostProcessingBehaviour") as MonoBehaviour;
  238. if ((pp != null) && pp.enabled)
  239. {
  240. pp.enabled = false;
  241. cam.Render();
  242. pp.enabled = true;
  243. }
  244. Graphics.DrawTexture(new Rect(w, 0, w, h), cam.targetTexture, alphaMat);
  245. // Restore settings.
  246. clipQuad.SetActive(false);
  247. if (behaviours != null)
  248. {
  249. for (int i = 0; i < behaviours.Length; i++)
  250. {
  251. if (wasEnabled[i])
  252. {
  253. behaviours[i].enabled = true;
  254. }
  255. }
  256. }
  257. cam.clearFlags = clearFlags;
  258. cam.backgroundColor = backgroundColor;
  259. }
  260. public void RenderFar()
  261. {
  262. cam.nearClipPlane = config.near;
  263. cam.farClipPlane = config.far;
  264. cam.Render();
  265. var w = Screen.width / 2;
  266. var h = Screen.height / 2;
  267. Graphics.DrawTexture(new Rect(0, h, w, h), cam.targetTexture, colorMat);
  268. }
  269. void OnGUI()
  270. {
  271. // Necessary for Graphics.DrawTexture to work even though we don't do anything here.
  272. }
  273. Camera[] cameras;
  274. Rect[] cameraRects;
  275. float sceneResolutionScale;
  276. void OnEnable()
  277. {
  278. // Move game view cameras to lower-right quadrant.
  279. cameras = FindObjectsOfType<Camera>();
  280. if (cameras != null)
  281. {
  282. var numCameras = cameras.Length;
  283. cameraRects = new Rect[numCameras];
  284. for (int i = 0; i < numCameras; i++)
  285. {
  286. var cam = cameras[i];
  287. cameraRects[i] = cam.rect;
  288. if (cam == this.cam)
  289. continue;
  290. if (cam.targetTexture != null)
  291. continue;
  292. if (cam.GetComponent<SteamVR_Camera>() != null)
  293. continue;
  294. cam.rect = new Rect(0.5f, 0.0f, 0.5f, 0.5f);
  295. }
  296. }
  297. if (config.sceneResolutionScale > 0.0f)
  298. {
  299. sceneResolutionScale = SteamVR_Camera.sceneResolutionScale;
  300. SteamVR_Camera.sceneResolutionScale = config.sceneResolutionScale;
  301. }
  302. if (autoEnableDisableActionSet)
  303. {
  304. SteamVR_Behaviour_Pose pose = this.GetComponentInChildren<SteamVR_Behaviour_Pose>();
  305. if (pose != null)
  306. {
  307. if (pose.poseAction.actionSet.IsActive(pose.inputSource) == false)
  308. {
  309. activatedActionSet = pose.poseAction.actionSet; //automatically activate the actionset if it isn't active already. (will deactivate on component disable)
  310. activatedInputSource = pose.inputSource;
  311. pose.poseAction.actionSet.Activate(pose.inputSource);
  312. }
  313. }
  314. }
  315. }
  316. void OnDisable()
  317. {
  318. if (autoEnableDisableActionSet)
  319. {
  320. if (activatedActionSet != null) //deactivate the action set we activated for this camera
  321. {
  322. activatedActionSet.Deactivate(activatedInputSource);
  323. activatedActionSet = null;
  324. }
  325. }
  326. // Restore game view cameras.
  327. if (cameras != null)
  328. {
  329. var numCameras = cameras.Length;
  330. for (int i = 0; i < numCameras; i++)
  331. {
  332. var cam = cameras[i];
  333. if (cam != null)
  334. cam.rect = cameraRects[i];
  335. }
  336. cameras = null;
  337. cameraRects = null;
  338. }
  339. if (config.sceneResolutionScale > 0.0f)
  340. {
  341. SteamVR_Camera.sceneResolutionScale = sceneResolutionScale;
  342. }
  343. }
  344. }
  345. }