123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- //======= Copyright (c) Valve Corporation, All rights reserved. ===============
- //
- // Purpose: Utilities for working with SteamVR
- //
- //=============================================================================
- using UnityEngine;
- using System.Collections;
- using System.Runtime.InteropServices;
- using Valve.VR;
- using System.IO;
- public static class SteamVR_Utils
- {
- public class Event
- {
- public delegate void Handler(params object[] args);
- public static void Listen(string message, Handler action)
- {
- var actions = listeners[message] as Handler;
- if (actions != null)
- {
- listeners[message] = actions + action;
- }
- else
- {
- listeners[message] = action;
- }
- }
- public static void Remove(string message, Handler action)
- {
- var actions = listeners[message] as Handler;
- if (actions != null)
- {
- listeners[message] = actions - action;
- }
- }
- public static void Send(string message, params object[] args)
- {
- var actions = listeners[message] as Handler;
- if (actions != null)
- {
- actions(args);
- }
- }
- private static Hashtable listeners = new Hashtable();
- }
- public static bool IsValid(Vector3 vector)
- {
- return (float.IsNaN(vector.x) == false && float.IsNaN(vector.y) == false && float.IsNaN(vector.z) == false);
- }
- public static bool IsValid(Quaternion rotation)
- {
- return (float.IsNaN(rotation.x) == false && float.IsNaN(rotation.y) == false && float.IsNaN(rotation.z) == false && float.IsNaN(rotation.w) == false) &&
- (rotation.x != 0 || rotation.y != 0 || rotation.z != 0 || rotation.w != 0);
- }
- // this version does not clamp [0..1]
- public static Quaternion Slerp(Quaternion A, Quaternion B, float t)
- {
- var cosom = Mathf.Clamp(A.x * B.x + A.y * B.y + A.z * B.z + A.w * B.w, -1.0f, 1.0f);
- if (cosom < 0.0f)
- {
- B = new Quaternion(-B.x, -B.y, -B.z, -B.w);
- cosom = -cosom;
- }
- float sclp, sclq;
- if ((1.0f - cosom) > 0.0001f)
- {
- var omega = Mathf.Acos(cosom);
- var sinom = Mathf.Sin(omega);
- sclp = Mathf.Sin((1.0f - t) * omega) / sinom;
- sclq = Mathf.Sin(t * omega) / sinom;
- }
- else
- {
- // "from" and "to" very close, so do linear interp
- sclp = 1.0f - t;
- sclq = t;
- }
- return new Quaternion(
- sclp * A.x + sclq * B.x,
- sclp * A.y + sclq * B.y,
- sclp * A.z + sclq * B.z,
- sclp * A.w + sclq * B.w);
- }
- public static Vector3 Lerp(Vector3 A, Vector3 B, float t)
- {
- return new Vector3(
- Lerp(A.x, B.x, t),
- Lerp(A.y, B.y, t),
- Lerp(A.z, B.z, t));
- }
- public static float Lerp(float A, float B, float t)
- {
- return A + (B - A) * t;
- }
- public static double Lerp(double A, double B, double t)
- {
- return A + (B - A) * t;
- }
- public static float InverseLerp(Vector3 A, Vector3 B, Vector3 result)
- {
- return Vector3.Dot(result - A, B - A);
- }
- public static float InverseLerp(float A, float B, float result)
- {
- return (result - A) / (B - A);
- }
- public static double InverseLerp(double A, double B, double result)
- {
- return (result - A) / (B - A);
- }
- public static float Saturate(float A)
- {
- return (A < 0) ? 0 : (A > 1) ? 1 : A;
- }
- public static Vector2 Saturate(Vector2 A)
- {
- return new Vector2(Saturate(A.x), Saturate(A.y));
- }
- public static float Abs(float A)
- {
- return (A < 0) ? -A : A;
- }
- public static Vector2 Abs(Vector2 A)
- {
- return new Vector2(Abs(A.x), Abs(A.y));
- }
- private static float _copysign(float sizeval, float signval)
- {
- return Mathf.Sign(signval) == 1 ? Mathf.Abs(sizeval) : -Mathf.Abs(sizeval);
- }
- public static Quaternion GetRotation(this Matrix4x4 matrix)
- {
- Quaternion q = new Quaternion();
- q.w = Mathf.Sqrt(Mathf.Max(0, 1 + matrix.m00 + matrix.m11 + matrix.m22)) / 2;
- q.x = Mathf.Sqrt(Mathf.Max(0, 1 + matrix.m00 - matrix.m11 - matrix.m22)) / 2;
- q.y = Mathf.Sqrt(Mathf.Max(0, 1 - matrix.m00 + matrix.m11 - matrix.m22)) / 2;
- q.z = Mathf.Sqrt(Mathf.Max(0, 1 - matrix.m00 - matrix.m11 + matrix.m22)) / 2;
- q.x = _copysign(q.x, matrix.m21 - matrix.m12);
- q.y = _copysign(q.y, matrix.m02 - matrix.m20);
- q.z = _copysign(q.z, matrix.m10 - matrix.m01);
- return q;
- }
- public static Vector3 GetPosition(this Matrix4x4 matrix)
- {
- var x = matrix.m03;
- var y = matrix.m13;
- var z = matrix.m23;
- return new Vector3(x, y, z);
- }
- public static Vector3 GetScale(this Matrix4x4 m)
- {
- var x = Mathf.Sqrt(m.m00 * m.m00 + m.m01 * m.m01 + m.m02 * m.m02);
- var y = Mathf.Sqrt(m.m10 * m.m10 + m.m11 * m.m11 + m.m12 * m.m12);
- var z = Mathf.Sqrt(m.m20 * m.m20 + m.m21 * m.m21 + m.m22 * m.m22);
- return new Vector3(x, y, z);
- }
- public static float GetLossyScale(Transform t)
- {
- return t.lossyScale.x;
- }
- private const string secretKey = "foobar";
- public static string GetBadMD5Hash(string usedString)
- {
- byte[] bytes = System.Text.Encoding.UTF8.GetBytes(usedString + secretKey);
- return GetBadMD5Hash(bytes);
- }
- public static string GetBadMD5Hash(byte[] bytes)
- {
- System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
- byte[] hash = md5.ComputeHash(bytes);
- System.Text.StringBuilder sb = new System.Text.StringBuilder();
- for (int i = 0; i < hash.Length; i++)
- {
- sb.Append(hash[i].ToString("x2"));
- }
- return sb.ToString();
- }
- public static string GetBadMD5HashFromFile(string filePath)
- {
- if (File.Exists(filePath) == false)
- return null;
- string data = File.ReadAllText(filePath);
- return GetBadMD5Hash(data + secretKey);
- }
- public static string SanitizePath(string path, bool allowLeadingSlash = true)
- {
- if (path.Contains("\\\\"))
- path = path.Replace("\\\\", "\\");
- if (path.Contains("//"))
- path = path.Replace("//", "/");
- if (allowLeadingSlash == false)
- {
- if (path[0] == '/' || path[0] == '\\')
- path = path.Substring(1);
- }
- return path;
- }
- public static System.Type FindType(string typeName)
- {
- var type = System.Type.GetType(typeName);
- if (type != null) return type;
- foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
- {
- type = a.GetType(typeName);
- if (type != null)
- return type;
- }
- return null;
- }
- [System.Serializable]
- public struct RigidTransform
- {
- public Vector3 pos;
- public Quaternion rot;
- public static RigidTransform identity
- {
- get { return new RigidTransform(Vector3.zero, Quaternion.identity); }
- }
- public static RigidTransform FromLocal(Transform t)
- {
- return new RigidTransform(t.localPosition, t.localRotation);
- }
- public RigidTransform(Vector3 pos, Quaternion rot)
- {
- this.pos = pos;
- this.rot = rot;
- }
- public RigidTransform(Transform t)
- {
- this.pos = t.position;
- this.rot = t.rotation;
- }
- public RigidTransform(Transform from, Transform to)
- {
- var inv = Quaternion.Inverse(from.rotation);
- rot = inv * to.rotation;
- pos = inv * (to.position - from.position);
- }
- public RigidTransform(HmdMatrix34_t pose)
- {
- var m = Matrix4x4.identity;
- m[0, 0] = pose.m0;
- m[0, 1] = pose.m1;
- m[0, 2] = -pose.m2;
- m[0, 3] = pose.m3;
- m[1, 0] = pose.m4;
- m[1, 1] = pose.m5;
- m[1, 2] = -pose.m6;
- m[1, 3] = pose.m7;
- m[2, 0] = -pose.m8;
- m[2, 1] = -pose.m9;
- m[2, 2] = pose.m10;
- m[2, 3] = -pose.m11;
- this.pos = m.GetPosition();
- this.rot = m.GetRotation();
- }
- public RigidTransform(HmdMatrix44_t pose)
- {
- var m = Matrix4x4.identity;
- m[0, 0] = pose.m0;
- m[0, 1] = pose.m1;
- m[0, 2] = -pose.m2;
- m[0, 3] = pose.m3;
- m[1, 0] = pose.m4;
- m[1, 1] = pose.m5;
- m[1, 2] = -pose.m6;
- m[1, 3] = pose.m7;
- m[2, 0] = -pose.m8;
- m[2, 1] = -pose.m9;
- m[2, 2] = pose.m10;
- m[2, 3] = -pose.m11;
- m[3, 0] = pose.m12;
- m[3, 1] = pose.m13;
- m[3, 2] = -pose.m14;
- m[3, 3] = pose.m15;
- this.pos = m.GetPosition();
- this.rot = m.GetRotation();
- }
- public HmdMatrix44_t ToHmdMatrix44()
- {
- var m = Matrix4x4.TRS(pos, rot, Vector3.one);
- var pose = new HmdMatrix44_t();
- pose.m0 = m[0, 0];
- pose.m1 = m[0, 1];
- pose.m2 = -m[0, 2];
- pose.m3 = m[0, 3];
- pose.m4 = m[1, 0];
- pose.m5 = m[1, 1];
- pose.m6 = -m[1, 2];
- pose.m7 = m[1, 3];
- pose.m8 = -m[2, 0];
- pose.m9 = -m[2, 1];
- pose.m10 = m[2, 2];
- pose.m11 = -m[2, 3];
- pose.m12 = m[3, 0];
- pose.m13 = m[3, 1];
- pose.m14 = -m[3, 2];
- pose.m15 = m[3, 3];
- return pose;
- }
- public HmdMatrix34_t ToHmdMatrix34()
- {
- var m = Matrix4x4.TRS(pos, rot, Vector3.one);
- var pose = new HmdMatrix34_t();
- pose.m0 = m[0, 0];
- pose.m1 = m[0, 1];
- pose.m2 = -m[0, 2];
- pose.m3 = m[0, 3];
- pose.m4 = m[1, 0];
- pose.m5 = m[1, 1];
- pose.m6 = -m[1, 2];
- pose.m7 = m[1, 3];
- pose.m8 = -m[2, 0];
- pose.m9 = -m[2, 1];
- pose.m10 = m[2, 2];
- pose.m11 = -m[2, 3];
- return pose;
- }
- public override bool Equals(object o)
- {
- if (o is RigidTransform)
- {
- RigidTransform t = (RigidTransform)o;
- return pos == t.pos && rot == t.rot;
- }
- return false;
- }
- public override int GetHashCode()
- {
- return pos.GetHashCode() ^ rot.GetHashCode();
- }
- public static bool operator ==(RigidTransform a, RigidTransform b)
- {
- return a.pos == b.pos && a.rot == b.rot;
- }
- public static bool operator !=(RigidTransform a, RigidTransform b)
- {
- return a.pos != b.pos || a.rot != b.rot;
- }
- public static RigidTransform operator *(RigidTransform a, RigidTransform b)
- {
- return new RigidTransform
- {
- rot = a.rot * b.rot,
- pos = a.pos + a.rot * b.pos
- };
- }
- public void Inverse()
- {
- rot = Quaternion.Inverse(rot);
- pos = -(rot * pos);
- }
- public RigidTransform GetInverse()
- {
- var t = new RigidTransform(pos, rot);
- t.Inverse();
- return t;
- }
- public void Multiply(RigidTransform a, RigidTransform b)
- {
- rot = a.rot * b.rot;
- pos = a.pos + a.rot * b.pos;
- }
- public Vector3 InverseTransformPoint(Vector3 point)
- {
- return Quaternion.Inverse(rot) * (point - pos);
- }
- public Vector3 TransformPoint(Vector3 point)
- {
- return pos + (rot * point);
- }
- public static Vector3 operator *(RigidTransform t, Vector3 v)
- {
- return t.TransformPoint(v);
- }
- public static RigidTransform Interpolate(RigidTransform a, RigidTransform b, float t)
- {
- return new RigidTransform(Vector3.Lerp(a.pos, b.pos, t), Quaternion.Slerp(a.rot, b.rot, t));
- }
- public void Interpolate(RigidTransform to, float t)
- {
- pos = SteamVR_Utils.Lerp(pos, to.pos, t);
- rot = SteamVR_Utils.Slerp(rot, to.rot, t);
- }
- }
- public delegate object SystemFn(CVRSystem system, params object[] args);
- public static object CallSystemFn(SystemFn fn, params object[] args)
- {
- var initOpenVR = (!SteamVR.active && !SteamVR.usingNativeSupport);
- if (initOpenVR)
- {
- var error = EVRInitError.None;
- OpenVR.Init(ref error, EVRApplicationType.VRApplication_Utility);
- }
- var system = OpenVR.System;
- var result = (system != null) ? fn(system, args) : null;
- if (initOpenVR)
- OpenVR.Shutdown();
- return result;
- }
- public static void TakeStereoScreenshot(uint screenshotHandle, GameObject target, int cellSize, float ipd, ref string previewFilename, ref string VRFilename)
- {
- const int width = 4096;
- const int height = width / 2;
- const int halfHeight = height / 2;
- var texture = new Texture2D(width, height * 2, TextureFormat.ARGB32, false);
- var timer = new System.Diagnostics.Stopwatch();
- Camera tempCamera = null;
- timer.Start();
- var camera = target.GetComponent<Camera>();
- if (camera == null)
- {
- if (tempCamera == null)
- tempCamera = new GameObject().AddComponent<Camera>();
- camera = tempCamera;
- }
- // Render preview texture
- const int previewWidth = 2048;
- const int previewHeight = 2048;
- var previewTexture = new Texture2D(previewWidth, previewHeight, TextureFormat.ARGB32, false);
- var targetPreviewTexture = new RenderTexture(previewWidth, previewHeight, 24);
- var oldTargetTexture = camera.targetTexture;
- var oldOrthographic = camera.orthographic;
- var oldFieldOfView = camera.fieldOfView;
- var oldAspect = camera.aspect;
- var oldstereoTargetEye = camera.stereoTargetEye;
- camera.stereoTargetEye = StereoTargetEyeMask.None;
- camera.fieldOfView = 60.0f;
- camera.orthographic = false;
- camera.targetTexture = targetPreviewTexture;
- camera.aspect = 1.0f;
- camera.Render();
- // copy preview texture
- RenderTexture.active = targetPreviewTexture;
- previewTexture.ReadPixels(new Rect(0, 0, targetPreviewTexture.width, targetPreviewTexture.height), 0, 0);
- RenderTexture.active = null;
- camera.targetTexture = null;
- Object.DestroyImmediate(targetPreviewTexture);
- var fx = camera.gameObject.AddComponent<SteamVR_SphericalProjection>();
- var oldPosition = target.transform.localPosition;
- var oldRotation = target.transform.localRotation;
- var basePosition = target.transform.position;
- var baseRotation = Quaternion.Euler(0, target.transform.rotation.eulerAngles.y, 0);
- var transform = camera.transform;
- int vTotal = halfHeight / cellSize;
- float dv = 90.0f / vTotal; // vertical degrees per segment
- float dvHalf = dv / 2.0f;
- var targetTexture = new RenderTexture(cellSize, cellSize, 24);
- targetTexture.wrapMode = TextureWrapMode.Clamp;
- targetTexture.antiAliasing = 8;
- camera.fieldOfView = dv;
- camera.orthographic = false;
- camera.targetTexture = targetTexture;
- camera.aspect = oldAspect;
- camera.stereoTargetEye = StereoTargetEyeMask.None;
- // Render sections of a sphere using a rectilinear projection
- // and resample using a sphereical projection into a single panorama
- // texture per eye. We break into sections in order to keep the eye
- // separation similar around the sphere. Rendering alternates between
- // top and bottom sections, sweeping horizontally around the sphere,
- // alternating left and right eyes.
- for (int v = 0; v < vTotal; v++)
- {
- var pitch = 90.0f - (v * dv) - dvHalf;
- var uTotal = width / targetTexture.width;
- var du = 360.0f / uTotal; // horizontal degrees per segment
- var duHalf = du / 2.0f;
- var vTarget = v * halfHeight / vTotal;
- for (int i = 0; i < 2; i++) // top, bottom
- {
- if (i == 1)
- {
- pitch = -pitch;
- vTarget = height - vTarget - cellSize;
- }
- for (int u = 0; u < uTotal; u++)
- {
- var yaw = -180.0f + (u * du) + duHalf;
- var uTarget = u * width / uTotal;
- var vTargetOffset = 0;
- var xOffset = -ipd / 2 * Mathf.Cos(pitch * Mathf.Deg2Rad);
- for (int j = 0; j < 2; j++) // left, right
- {
- if (j == 1)
- {
- vTargetOffset = height;
- xOffset = -xOffset;
- }
- var offset = baseRotation * Quaternion.Euler(0, yaw, 0) * new Vector3(xOffset, 0, 0);
- transform.position = basePosition + offset;
- var direction = Quaternion.Euler(pitch, yaw, 0.0f);
- transform.rotation = baseRotation * direction;
- // vector pointing to center of this section
- var N = direction * Vector3.forward;
- // horizontal span of this section in degrees
- var phi0 = yaw - (du / 2);
- var phi1 = phi0 + du;
- // vertical span of this section in degrees
- var theta0 = pitch + (dv / 2);
- var theta1 = theta0 - dv;
- var midPhi = (phi0 + phi1) / 2;
- var baseTheta = Mathf.Abs(theta0) < Mathf.Abs(theta1) ? theta0 : theta1;
- // vectors pointing to corners of image closes to the equator
- var V00 = Quaternion.Euler(baseTheta, phi0, 0.0f) * Vector3.forward;
- var V01 = Quaternion.Euler(baseTheta, phi1, 0.0f) * Vector3.forward;
- // vectors pointing to top and bottom midsection of image
- var V0M = Quaternion.Euler(theta0, midPhi, 0.0f) * Vector3.forward;
- var V1M = Quaternion.Euler(theta1, midPhi, 0.0f) * Vector3.forward;
- // intersection points for each of the above
- var P00 = V00 / Vector3.Dot(V00, N);
- var P01 = V01 / Vector3.Dot(V01, N);
- var P0M = V0M / Vector3.Dot(V0M, N);
- var P1M = V1M / Vector3.Dot(V1M, N);
- // calculate basis vectors for plane
- var P00_P01 = P01 - P00;
- var P0M_P1M = P1M - P0M;
- var uMag = P00_P01.magnitude;
- var vMag = P0M_P1M.magnitude;
- var uScale = 1.0f / uMag;
- var vScale = 1.0f / vMag;
- var uAxis = P00_P01 * uScale;
- var vAxis = P0M_P1M * vScale;
- // update material constant buffer
- fx.Set(N, phi0, phi1, theta0, theta1,
- uAxis, P00, uScale,
- vAxis, P0M, vScale);
- camera.aspect = uMag / vMag;
- camera.Render();
- RenderTexture.active = targetTexture;
- texture.ReadPixels(new Rect(0, 0, targetTexture.width, targetTexture.height), uTarget, vTarget + vTargetOffset);
- RenderTexture.active = null;
- }
- // Update progress
- var progress = (float)(v * (uTotal * 2.0f) + u + i * uTotal) / (float)(vTotal * (uTotal * 2.0f));
- OpenVR.Screenshots.UpdateScreenshotProgress(screenshotHandle, progress);
- }
- }
- }
- // 100% flush
- OpenVR.Screenshots.UpdateScreenshotProgress(screenshotHandle, 1.0f);
- // Save textures to disk.
- // Add extensions
- previewFilename += ".png";
- VRFilename += ".png";
- // Preview
- previewTexture.Apply();
- System.IO.File.WriteAllBytes(previewFilename, previewTexture.EncodeToPNG());
- // VR
- texture.Apply();
- System.IO.File.WriteAllBytes(VRFilename, texture.EncodeToPNG());
- // Cleanup.
- if (camera != tempCamera)
- {
- camera.targetTexture = oldTargetTexture;
- camera.orthographic = oldOrthographic;
- camera.fieldOfView = oldFieldOfView;
- camera.aspect = oldAspect;
- camera.stereoTargetEye = oldstereoTargetEye;
- target.transform.localPosition = oldPosition;
- target.transform.localRotation = oldRotation;
- }
- else
- {
- tempCamera.targetTexture = null;
- }
- Object.DestroyImmediate(targetTexture);
- Object.DestroyImmediate(fx);
- timer.Stop();
- Debug.Log(string.Format("Screenshot took {0} seconds.", timer.Elapsed));
- if (tempCamera != null)
- {
- Object.DestroyImmediate(tempCamera.gameObject);
- }
- Object.DestroyImmediate(previewTexture);
- Object.DestroyImmediate(texture);
- }
- }
|