SteamVR_ExternalCamera.cs 9.4 KB

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