SteamVR_Utils.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Utilities for working with SteamVR
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using System.Collections;
  8. using System.Runtime.InteropServices;
  9. using Valve.VR;
  10. using System.IO;
  11. public static class SteamVR_Utils
  12. {
  13. public class Event
  14. {
  15. public delegate void Handler(params object[] args);
  16. public static void Listen(string message, Handler action)
  17. {
  18. var actions = listeners[message] as Handler;
  19. if (actions != null)
  20. {
  21. listeners[message] = actions + action;
  22. }
  23. else
  24. {
  25. listeners[message] = action;
  26. }
  27. }
  28. public static void Remove(string message, Handler action)
  29. {
  30. var actions = listeners[message] as Handler;
  31. if (actions != null)
  32. {
  33. listeners[message] = actions - action;
  34. }
  35. }
  36. public static void Send(string message, params object[] args)
  37. {
  38. var actions = listeners[message] as Handler;
  39. if (actions != null)
  40. {
  41. actions(args);
  42. }
  43. }
  44. private static Hashtable listeners = new Hashtable();
  45. }
  46. public static bool IsValid(Vector3 vector)
  47. {
  48. return (float.IsNaN(vector.x) == false && float.IsNaN(vector.y) == false && float.IsNaN(vector.z) == false);
  49. }
  50. public static bool IsValid(Quaternion rotation)
  51. {
  52. return (float.IsNaN(rotation.x) == false && float.IsNaN(rotation.y) == false && float.IsNaN(rotation.z) == false && float.IsNaN(rotation.w) == false) &&
  53. (rotation.x != 0 || rotation.y != 0 || rotation.z != 0 || rotation.w != 0);
  54. }
  55. // this version does not clamp [0..1]
  56. public static Quaternion Slerp(Quaternion A, Quaternion B, float t)
  57. {
  58. var cosom = Mathf.Clamp(A.x * B.x + A.y * B.y + A.z * B.z + A.w * B.w, -1.0f, 1.0f);
  59. if (cosom < 0.0f)
  60. {
  61. B = new Quaternion(-B.x, -B.y, -B.z, -B.w);
  62. cosom = -cosom;
  63. }
  64. float sclp, sclq;
  65. if ((1.0f - cosom) > 0.0001f)
  66. {
  67. var omega = Mathf.Acos(cosom);
  68. var sinom = Mathf.Sin(omega);
  69. sclp = Mathf.Sin((1.0f - t) * omega) / sinom;
  70. sclq = Mathf.Sin(t * omega) / sinom;
  71. }
  72. else
  73. {
  74. // "from" and "to" very close, so do linear interp
  75. sclp = 1.0f - t;
  76. sclq = t;
  77. }
  78. return new Quaternion(
  79. sclp * A.x + sclq * B.x,
  80. sclp * A.y + sclq * B.y,
  81. sclp * A.z + sclq * B.z,
  82. sclp * A.w + sclq * B.w);
  83. }
  84. public static Vector3 Lerp(Vector3 A, Vector3 B, float t)
  85. {
  86. return new Vector3(
  87. Lerp(A.x, B.x, t),
  88. Lerp(A.y, B.y, t),
  89. Lerp(A.z, B.z, t));
  90. }
  91. public static float Lerp(float A, float B, float t)
  92. {
  93. return A + (B - A) * t;
  94. }
  95. public static double Lerp(double A, double B, double t)
  96. {
  97. return A + (B - A) * t;
  98. }
  99. public static float InverseLerp(Vector3 A, Vector3 B, Vector3 result)
  100. {
  101. return Vector3.Dot(result - A, B - A);
  102. }
  103. public static float InverseLerp(float A, float B, float result)
  104. {
  105. return (result - A) / (B - A);
  106. }
  107. public static double InverseLerp(double A, double B, double result)
  108. {
  109. return (result - A) / (B - A);
  110. }
  111. public static float Saturate(float A)
  112. {
  113. return (A < 0) ? 0 : (A > 1) ? 1 : A;
  114. }
  115. public static Vector2 Saturate(Vector2 A)
  116. {
  117. return new Vector2(Saturate(A.x), Saturate(A.y));
  118. }
  119. public static float Abs(float A)
  120. {
  121. return (A < 0) ? -A : A;
  122. }
  123. public static Vector2 Abs(Vector2 A)
  124. {
  125. return new Vector2(Abs(A.x), Abs(A.y));
  126. }
  127. private static float _copysign(float sizeval, float signval)
  128. {
  129. return Mathf.Sign(signval) == 1 ? Mathf.Abs(sizeval) : -Mathf.Abs(sizeval);
  130. }
  131. public static Quaternion GetRotation(this Matrix4x4 matrix)
  132. {
  133. Quaternion q = new Quaternion();
  134. q.w = Mathf.Sqrt(Mathf.Max(0, 1 + matrix.m00 + matrix.m11 + matrix.m22)) / 2;
  135. q.x = Mathf.Sqrt(Mathf.Max(0, 1 + matrix.m00 - matrix.m11 - matrix.m22)) / 2;
  136. q.y = Mathf.Sqrt(Mathf.Max(0, 1 - matrix.m00 + matrix.m11 - matrix.m22)) / 2;
  137. q.z = Mathf.Sqrt(Mathf.Max(0, 1 - matrix.m00 - matrix.m11 + matrix.m22)) / 2;
  138. q.x = _copysign(q.x, matrix.m21 - matrix.m12);
  139. q.y = _copysign(q.y, matrix.m02 - matrix.m20);
  140. q.z = _copysign(q.z, matrix.m10 - matrix.m01);
  141. return q;
  142. }
  143. public static Vector3 GetPosition(this Matrix4x4 matrix)
  144. {
  145. var x = matrix.m03;
  146. var y = matrix.m13;
  147. var z = matrix.m23;
  148. return new Vector3(x, y, z);
  149. }
  150. public static Vector3 GetScale(this Matrix4x4 m)
  151. {
  152. var x = Mathf.Sqrt(m.m00 * m.m00 + m.m01 * m.m01 + m.m02 * m.m02);
  153. var y = Mathf.Sqrt(m.m10 * m.m10 + m.m11 * m.m11 + m.m12 * m.m12);
  154. var z = Mathf.Sqrt(m.m20 * m.m20 + m.m21 * m.m21 + m.m22 * m.m22);
  155. return new Vector3(x, y, z);
  156. }
  157. public static float GetLossyScale(Transform t)
  158. {
  159. return t.lossyScale.x;
  160. }
  161. private const string secretKey = "foobar";
  162. public static string GetBadMD5Hash(string usedString)
  163. {
  164. byte[] bytes = System.Text.Encoding.UTF8.GetBytes(usedString + secretKey);
  165. return GetBadMD5Hash(bytes);
  166. }
  167. public static string GetBadMD5Hash(byte[] bytes)
  168. {
  169. System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
  170. byte[] hash = md5.ComputeHash(bytes);
  171. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  172. for (int i = 0; i < hash.Length; i++)
  173. {
  174. sb.Append(hash[i].ToString("x2"));
  175. }
  176. return sb.ToString();
  177. }
  178. public static string GetBadMD5HashFromFile(string filePath)
  179. {
  180. if (File.Exists(filePath) == false)
  181. return null;
  182. string data = File.ReadAllText(filePath);
  183. return GetBadMD5Hash(data + secretKey);
  184. }
  185. public static string SanitizePath(string path, bool allowLeadingSlash = true)
  186. {
  187. if (path.Contains("\\\\"))
  188. path = path.Replace("\\\\", "\\");
  189. if (path.Contains("//"))
  190. path = path.Replace("//", "/");
  191. if (allowLeadingSlash == false)
  192. {
  193. if (path[0] == '/' || path[0] == '\\')
  194. path = path.Substring(1);
  195. }
  196. return path;
  197. }
  198. public static System.Type FindType(string typeName)
  199. {
  200. var type = System.Type.GetType(typeName);
  201. if (type != null) return type;
  202. foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
  203. {
  204. type = a.GetType(typeName);
  205. if (type != null)
  206. return type;
  207. }
  208. return null;
  209. }
  210. [System.Serializable]
  211. public struct RigidTransform
  212. {
  213. public Vector3 pos;
  214. public Quaternion rot;
  215. public static RigidTransform identity
  216. {
  217. get { return new RigidTransform(Vector3.zero, Quaternion.identity); }
  218. }
  219. public static RigidTransform FromLocal(Transform t)
  220. {
  221. return new RigidTransform(t.localPosition, t.localRotation);
  222. }
  223. public RigidTransform(Vector3 pos, Quaternion rot)
  224. {
  225. this.pos = pos;
  226. this.rot = rot;
  227. }
  228. public RigidTransform(Transform t)
  229. {
  230. this.pos = t.position;
  231. this.rot = t.rotation;
  232. }
  233. public RigidTransform(Transform from, Transform to)
  234. {
  235. var inv = Quaternion.Inverse(from.rotation);
  236. rot = inv * to.rotation;
  237. pos = inv * (to.position - from.position);
  238. }
  239. public RigidTransform(HmdMatrix34_t pose)
  240. {
  241. var m = Matrix4x4.identity;
  242. m[0, 0] = pose.m0;
  243. m[0, 1] = pose.m1;
  244. m[0, 2] = -pose.m2;
  245. m[0, 3] = pose.m3;
  246. m[1, 0] = pose.m4;
  247. m[1, 1] = pose.m5;
  248. m[1, 2] = -pose.m6;
  249. m[1, 3] = pose.m7;
  250. m[2, 0] = -pose.m8;
  251. m[2, 1] = -pose.m9;
  252. m[2, 2] = pose.m10;
  253. m[2, 3] = -pose.m11;
  254. this.pos = m.GetPosition();
  255. this.rot = m.GetRotation();
  256. }
  257. public RigidTransform(HmdMatrix44_t pose)
  258. {
  259. var m = Matrix4x4.identity;
  260. m[0, 0] = pose.m0;
  261. m[0, 1] = pose.m1;
  262. m[0, 2] = -pose.m2;
  263. m[0, 3] = pose.m3;
  264. m[1, 0] = pose.m4;
  265. m[1, 1] = pose.m5;
  266. m[1, 2] = -pose.m6;
  267. m[1, 3] = pose.m7;
  268. m[2, 0] = -pose.m8;
  269. m[2, 1] = -pose.m9;
  270. m[2, 2] = pose.m10;
  271. m[2, 3] = -pose.m11;
  272. m[3, 0] = pose.m12;
  273. m[3, 1] = pose.m13;
  274. m[3, 2] = -pose.m14;
  275. m[3, 3] = pose.m15;
  276. this.pos = m.GetPosition();
  277. this.rot = m.GetRotation();
  278. }
  279. public HmdMatrix44_t ToHmdMatrix44()
  280. {
  281. var m = Matrix4x4.TRS(pos, rot, Vector3.one);
  282. var pose = new HmdMatrix44_t();
  283. pose.m0 = m[0, 0];
  284. pose.m1 = m[0, 1];
  285. pose.m2 = -m[0, 2];
  286. pose.m3 = m[0, 3];
  287. pose.m4 = m[1, 0];
  288. pose.m5 = m[1, 1];
  289. pose.m6 = -m[1, 2];
  290. pose.m7 = m[1, 3];
  291. pose.m8 = -m[2, 0];
  292. pose.m9 = -m[2, 1];
  293. pose.m10 = m[2, 2];
  294. pose.m11 = -m[2, 3];
  295. pose.m12 = m[3, 0];
  296. pose.m13 = m[3, 1];
  297. pose.m14 = -m[3, 2];
  298. pose.m15 = m[3, 3];
  299. return pose;
  300. }
  301. public HmdMatrix34_t ToHmdMatrix34()
  302. {
  303. var m = Matrix4x4.TRS(pos, rot, Vector3.one);
  304. var pose = new HmdMatrix34_t();
  305. pose.m0 = m[0, 0];
  306. pose.m1 = m[0, 1];
  307. pose.m2 = -m[0, 2];
  308. pose.m3 = m[0, 3];
  309. pose.m4 = m[1, 0];
  310. pose.m5 = m[1, 1];
  311. pose.m6 = -m[1, 2];
  312. pose.m7 = m[1, 3];
  313. pose.m8 = -m[2, 0];
  314. pose.m9 = -m[2, 1];
  315. pose.m10 = m[2, 2];
  316. pose.m11 = -m[2, 3];
  317. return pose;
  318. }
  319. public override bool Equals(object o)
  320. {
  321. if (o is RigidTransform)
  322. {
  323. RigidTransform t = (RigidTransform)o;
  324. return pos == t.pos && rot == t.rot;
  325. }
  326. return false;
  327. }
  328. public override int GetHashCode()
  329. {
  330. return pos.GetHashCode() ^ rot.GetHashCode();
  331. }
  332. public static bool operator ==(RigidTransform a, RigidTransform b)
  333. {
  334. return a.pos == b.pos && a.rot == b.rot;
  335. }
  336. public static bool operator !=(RigidTransform a, RigidTransform b)
  337. {
  338. return a.pos != b.pos || a.rot != b.rot;
  339. }
  340. public static RigidTransform operator *(RigidTransform a, RigidTransform b)
  341. {
  342. return new RigidTransform
  343. {
  344. rot = a.rot * b.rot,
  345. pos = a.pos + a.rot * b.pos
  346. };
  347. }
  348. public void Inverse()
  349. {
  350. rot = Quaternion.Inverse(rot);
  351. pos = -(rot * pos);
  352. }
  353. public RigidTransform GetInverse()
  354. {
  355. var t = new RigidTransform(pos, rot);
  356. t.Inverse();
  357. return t;
  358. }
  359. public void Multiply(RigidTransform a, RigidTransform b)
  360. {
  361. rot = a.rot * b.rot;
  362. pos = a.pos + a.rot * b.pos;
  363. }
  364. public Vector3 InverseTransformPoint(Vector3 point)
  365. {
  366. return Quaternion.Inverse(rot) * (point - pos);
  367. }
  368. public Vector3 TransformPoint(Vector3 point)
  369. {
  370. return pos + (rot * point);
  371. }
  372. public static Vector3 operator *(RigidTransform t, Vector3 v)
  373. {
  374. return t.TransformPoint(v);
  375. }
  376. public static RigidTransform Interpolate(RigidTransform a, RigidTransform b, float t)
  377. {
  378. return new RigidTransform(Vector3.Lerp(a.pos, b.pos, t), Quaternion.Slerp(a.rot, b.rot, t));
  379. }
  380. public void Interpolate(RigidTransform to, float t)
  381. {
  382. pos = SteamVR_Utils.Lerp(pos, to.pos, t);
  383. rot = SteamVR_Utils.Slerp(rot, to.rot, t);
  384. }
  385. }
  386. public delegate object SystemFn(CVRSystem system, params object[] args);
  387. public static object CallSystemFn(SystemFn fn, params object[] args)
  388. {
  389. var initOpenVR = (!SteamVR.active && !SteamVR.usingNativeSupport);
  390. if (initOpenVR)
  391. {
  392. var error = EVRInitError.None;
  393. OpenVR.Init(ref error, EVRApplicationType.VRApplication_Utility);
  394. }
  395. var system = OpenVR.System;
  396. var result = (system != null) ? fn(system, args) : null;
  397. if (initOpenVR)
  398. OpenVR.Shutdown();
  399. return result;
  400. }
  401. public static void TakeStereoScreenshot(uint screenshotHandle, GameObject target, int cellSize, float ipd, ref string previewFilename, ref string VRFilename)
  402. {
  403. const int width = 4096;
  404. const int height = width / 2;
  405. const int halfHeight = height / 2;
  406. var texture = new Texture2D(width, height * 2, TextureFormat.ARGB32, false);
  407. var timer = new System.Diagnostics.Stopwatch();
  408. Camera tempCamera = null;
  409. timer.Start();
  410. var camera = target.GetComponent<Camera>();
  411. if (camera == null)
  412. {
  413. if (tempCamera == null)
  414. tempCamera = new GameObject().AddComponent<Camera>();
  415. camera = tempCamera;
  416. }
  417. // Render preview texture
  418. const int previewWidth = 2048;
  419. const int previewHeight = 2048;
  420. var previewTexture = new Texture2D(previewWidth, previewHeight, TextureFormat.ARGB32, false);
  421. var targetPreviewTexture = new RenderTexture(previewWidth, previewHeight, 24);
  422. var oldTargetTexture = camera.targetTexture;
  423. var oldOrthographic = camera.orthographic;
  424. var oldFieldOfView = camera.fieldOfView;
  425. var oldAspect = camera.aspect;
  426. var oldstereoTargetEye = camera.stereoTargetEye;
  427. camera.stereoTargetEye = StereoTargetEyeMask.None;
  428. camera.fieldOfView = 60.0f;
  429. camera.orthographic = false;
  430. camera.targetTexture = targetPreviewTexture;
  431. camera.aspect = 1.0f;
  432. camera.Render();
  433. // copy preview texture
  434. RenderTexture.active = targetPreviewTexture;
  435. previewTexture.ReadPixels(new Rect(0, 0, targetPreviewTexture.width, targetPreviewTexture.height), 0, 0);
  436. RenderTexture.active = null;
  437. camera.targetTexture = null;
  438. Object.DestroyImmediate(targetPreviewTexture);
  439. var fx = camera.gameObject.AddComponent<SteamVR_SphericalProjection>();
  440. var oldPosition = target.transform.localPosition;
  441. var oldRotation = target.transform.localRotation;
  442. var basePosition = target.transform.position;
  443. var baseRotation = Quaternion.Euler(0, target.transform.rotation.eulerAngles.y, 0);
  444. var transform = camera.transform;
  445. int vTotal = halfHeight / cellSize;
  446. float dv = 90.0f / vTotal; // vertical degrees per segment
  447. float dvHalf = dv / 2.0f;
  448. var targetTexture = new RenderTexture(cellSize, cellSize, 24);
  449. targetTexture.wrapMode = TextureWrapMode.Clamp;
  450. targetTexture.antiAliasing = 8;
  451. camera.fieldOfView = dv;
  452. camera.orthographic = false;
  453. camera.targetTexture = targetTexture;
  454. camera.aspect = oldAspect;
  455. camera.stereoTargetEye = StereoTargetEyeMask.None;
  456. // Render sections of a sphere using a rectilinear projection
  457. // and resample using a sphereical projection into a single panorama
  458. // texture per eye. We break into sections in order to keep the eye
  459. // separation similar around the sphere. Rendering alternates between
  460. // top and bottom sections, sweeping horizontally around the sphere,
  461. // alternating left and right eyes.
  462. for (int v = 0; v < vTotal; v++)
  463. {
  464. var pitch = 90.0f - (v * dv) - dvHalf;
  465. var uTotal = width / targetTexture.width;
  466. var du = 360.0f / uTotal; // horizontal degrees per segment
  467. var duHalf = du / 2.0f;
  468. var vTarget = v * halfHeight / vTotal;
  469. for (int i = 0; i < 2; i++) // top, bottom
  470. {
  471. if (i == 1)
  472. {
  473. pitch = -pitch;
  474. vTarget = height - vTarget - cellSize;
  475. }
  476. for (int u = 0; u < uTotal; u++)
  477. {
  478. var yaw = -180.0f + (u * du) + duHalf;
  479. var uTarget = u * width / uTotal;
  480. var vTargetOffset = 0;
  481. var xOffset = -ipd / 2 * Mathf.Cos(pitch * Mathf.Deg2Rad);
  482. for (int j = 0; j < 2; j++) // left, right
  483. {
  484. if (j == 1)
  485. {
  486. vTargetOffset = height;
  487. xOffset = -xOffset;
  488. }
  489. var offset = baseRotation * Quaternion.Euler(0, yaw, 0) * new Vector3(xOffset, 0, 0);
  490. transform.position = basePosition + offset;
  491. var direction = Quaternion.Euler(pitch, yaw, 0.0f);
  492. transform.rotation = baseRotation * direction;
  493. // vector pointing to center of this section
  494. var N = direction * Vector3.forward;
  495. // horizontal span of this section in degrees
  496. var phi0 = yaw - (du / 2);
  497. var phi1 = phi0 + du;
  498. // vertical span of this section in degrees
  499. var theta0 = pitch + (dv / 2);
  500. var theta1 = theta0 - dv;
  501. var midPhi = (phi0 + phi1) / 2;
  502. var baseTheta = Mathf.Abs(theta0) < Mathf.Abs(theta1) ? theta0 : theta1;
  503. // vectors pointing to corners of image closes to the equator
  504. var V00 = Quaternion.Euler(baseTheta, phi0, 0.0f) * Vector3.forward;
  505. var V01 = Quaternion.Euler(baseTheta, phi1, 0.0f) * Vector3.forward;
  506. // vectors pointing to top and bottom midsection of image
  507. var V0M = Quaternion.Euler(theta0, midPhi, 0.0f) * Vector3.forward;
  508. var V1M = Quaternion.Euler(theta1, midPhi, 0.0f) * Vector3.forward;
  509. // intersection points for each of the above
  510. var P00 = V00 / Vector3.Dot(V00, N);
  511. var P01 = V01 / Vector3.Dot(V01, N);
  512. var P0M = V0M / Vector3.Dot(V0M, N);
  513. var P1M = V1M / Vector3.Dot(V1M, N);
  514. // calculate basis vectors for plane
  515. var P00_P01 = P01 - P00;
  516. var P0M_P1M = P1M - P0M;
  517. var uMag = P00_P01.magnitude;
  518. var vMag = P0M_P1M.magnitude;
  519. var uScale = 1.0f / uMag;
  520. var vScale = 1.0f / vMag;
  521. var uAxis = P00_P01 * uScale;
  522. var vAxis = P0M_P1M * vScale;
  523. // update material constant buffer
  524. fx.Set(N, phi0, phi1, theta0, theta1,
  525. uAxis, P00, uScale,
  526. vAxis, P0M, vScale);
  527. camera.aspect = uMag / vMag;
  528. camera.Render();
  529. RenderTexture.active = targetTexture;
  530. texture.ReadPixels(new Rect(0, 0, targetTexture.width, targetTexture.height), uTarget, vTarget + vTargetOffset);
  531. RenderTexture.active = null;
  532. }
  533. // Update progress
  534. var progress = (float)(v * (uTotal * 2.0f) + u + i * uTotal) / (float)(vTotal * (uTotal * 2.0f));
  535. OpenVR.Screenshots.UpdateScreenshotProgress(screenshotHandle, progress);
  536. }
  537. }
  538. }
  539. // 100% flush
  540. OpenVR.Screenshots.UpdateScreenshotProgress(screenshotHandle, 1.0f);
  541. // Save textures to disk.
  542. // Add extensions
  543. previewFilename += ".png";
  544. VRFilename += ".png";
  545. // Preview
  546. previewTexture.Apply();
  547. System.IO.File.WriteAllBytes(previewFilename, previewTexture.EncodeToPNG());
  548. // VR
  549. texture.Apply();
  550. System.IO.File.WriteAllBytes(VRFilename, texture.EncodeToPNG());
  551. // Cleanup.
  552. if (camera != tempCamera)
  553. {
  554. camera.targetTexture = oldTargetTexture;
  555. camera.orthographic = oldOrthographic;
  556. camera.fieldOfView = oldFieldOfView;
  557. camera.aspect = oldAspect;
  558. camera.stereoTargetEye = oldstereoTargetEye;
  559. target.transform.localPosition = oldPosition;
  560. target.transform.localRotation = oldRotation;
  561. }
  562. else
  563. {
  564. tempCamera.targetTexture = null;
  565. }
  566. Object.DestroyImmediate(targetTexture);
  567. Object.DestroyImmediate(fx);
  568. timer.Stop();
  569. Debug.Log(string.Format("Screenshot took {0} seconds.", timer.Elapsed));
  570. if (tempCamera != null)
  571. {
  572. Object.DestroyImmediate(tempCamera.gameObject);
  573. }
  574. Object.DestroyImmediate(previewTexture);
  575. Object.DestroyImmediate(texture);
  576. }
  577. }