SteamVR_Utils.cs 27 KB

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