SteamVR_PlayArea.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Draws different sized room-scale play areas for targeting content
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using UnityEngine.Rendering;
  8. using System.Collections;
  9. using Valve.VR;
  10. namespace Valve.VR
  11. {
  12. [ExecuteInEditMode, RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
  13. public class SteamVR_PlayArea : MonoBehaviour
  14. {
  15. public float borderThickness = 0.15f;
  16. public float wireframeHeight = 2.0f;
  17. public bool drawWireframeWhenSelectedOnly = false;
  18. public bool drawInGame = true;
  19. public enum Size
  20. {
  21. Calibrated,
  22. _400x300,
  23. _300x225,
  24. _200x150
  25. }
  26. public Size size;
  27. public Color color = Color.cyan;
  28. [HideInInspector]
  29. public Vector3[] vertices;
  30. public static bool GetBounds(Size size, ref HmdQuad_t pRect)
  31. {
  32. if (size == Size.Calibrated)
  33. {
  34. bool temporarySession = false;
  35. if (Application.isEditor && Application.isPlaying == false)
  36. temporarySession = SteamVR.InitializeTemporarySession();
  37. var chaperone = OpenVR.Chaperone;
  38. bool success = (chaperone != null) && chaperone.GetPlayAreaRect(ref pRect);
  39. if (!success)
  40. Debug.LogWarning("<b>[SteamVR]</b> Failed to get Calibrated Play Area bounds! Make sure you have tracking first, and that your space is calibrated.");
  41. if (temporarySession)
  42. SteamVR.ExitTemporarySession();
  43. return success;
  44. }
  45. else
  46. {
  47. try
  48. {
  49. var str = size.ToString().Substring(1);
  50. var arr = str.Split(new char[] { 'x' }, 2);
  51. // convert to half size in meters (from cm)
  52. var x = float.Parse(arr[0]) / 200;
  53. var z = float.Parse(arr[1]) / 200;
  54. pRect.vCorners0.v0 = x;
  55. pRect.vCorners0.v1 = 0;
  56. pRect.vCorners0.v2 = -z;
  57. pRect.vCorners1.v0 = -x;
  58. pRect.vCorners1.v1 = 0;
  59. pRect.vCorners1.v2 = -z;
  60. pRect.vCorners2.v0 = -x;
  61. pRect.vCorners2.v1 = 0;
  62. pRect.vCorners2.v2 = z;
  63. pRect.vCorners3.v0 = x;
  64. pRect.vCorners3.v1 = 0;
  65. pRect.vCorners3.v2 = z;
  66. return true;
  67. }
  68. catch { }
  69. }
  70. return false;
  71. }
  72. public void BuildMesh()
  73. {
  74. var rect = new HmdQuad_t();
  75. if (!GetBounds(size, ref rect))
  76. return;
  77. var corners = new HmdVector3_t[] { rect.vCorners0, rect.vCorners1, rect.vCorners2, rect.vCorners3 };
  78. vertices = new Vector3[corners.Length * 2];
  79. for (int i = 0; i < corners.Length; i++)
  80. {
  81. var c = corners[i];
  82. vertices[i] = new Vector3(c.v0, 0.01f, c.v2);
  83. }
  84. if (borderThickness == 0.0f)
  85. {
  86. GetComponent<MeshFilter>().mesh = null;
  87. return;
  88. }
  89. for (int i = 0; i < corners.Length; i++)
  90. {
  91. int next = (i + 1) % corners.Length;
  92. int prev = (i + corners.Length - 1) % corners.Length;
  93. var nextSegment = (vertices[next] - vertices[i]).normalized;
  94. var prevSegment = (vertices[prev] - vertices[i]).normalized;
  95. var vert = vertices[i];
  96. vert += Vector3.Cross(nextSegment, Vector3.up) * borderThickness;
  97. vert += Vector3.Cross(prevSegment, Vector3.down) * borderThickness;
  98. vertices[corners.Length + i] = vert;
  99. }
  100. var triangles = new int[]
  101. {
  102. 0, 4, 1,
  103. 1, 4, 5,
  104. 1, 5, 2,
  105. 2, 5, 6,
  106. 2, 6, 3,
  107. 3, 6, 7,
  108. 3, 7, 0,
  109. 0, 7, 4
  110. };
  111. var uv = new Vector2[]
  112. {
  113. new Vector2(0.0f, 0.0f),
  114. new Vector2(1.0f, 0.0f),
  115. new Vector2(0.0f, 0.0f),
  116. new Vector2(1.0f, 0.0f),
  117. new Vector2(0.0f, 1.0f),
  118. new Vector2(1.0f, 1.0f),
  119. new Vector2(0.0f, 1.0f),
  120. new Vector2(1.0f, 1.0f)
  121. };
  122. var colors = new Color[]
  123. {
  124. color,
  125. color,
  126. color,
  127. color,
  128. new Color(color.r, color.g, color.b, 0.0f),
  129. new Color(color.r, color.g, color.b, 0.0f),
  130. new Color(color.r, color.g, color.b, 0.0f),
  131. new Color(color.r, color.g, color.b, 0.0f)
  132. };
  133. var mesh = new Mesh();
  134. GetComponent<MeshFilter>().mesh = mesh;
  135. mesh.vertices = vertices;
  136. mesh.uv = uv;
  137. mesh.colors = colors;
  138. mesh.triangles = triangles;
  139. var renderer = GetComponent<MeshRenderer>();
  140. renderer.material = new Material(Shader.Find("Sprites/Default"));
  141. renderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
  142. renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  143. renderer.receiveShadows = false;
  144. renderer.lightProbeUsage = LightProbeUsage.Off;
  145. }
  146. #if UNITY_EDITOR
  147. Hashtable values;
  148. void Update()
  149. {
  150. if (!Application.isPlaying)
  151. {
  152. var fields = GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
  153. bool rebuild = false;
  154. if (values == null || (borderThickness != 0.0f && GetComponent<MeshFilter>().sharedMesh == null))
  155. {
  156. rebuild = true;
  157. }
  158. else
  159. {
  160. foreach (var f in fields)
  161. {
  162. if (!values.Contains(f) || !f.GetValue(this).Equals(values[f]))
  163. {
  164. rebuild = true;
  165. break;
  166. }
  167. }
  168. }
  169. if (rebuild)
  170. {
  171. BuildMesh();
  172. values = new Hashtable();
  173. foreach (var f in fields)
  174. values[f] = f.GetValue(this);
  175. }
  176. }
  177. }
  178. #endif
  179. void OnDrawGizmos()
  180. {
  181. if (!drawWireframeWhenSelectedOnly)
  182. DrawWireframe();
  183. }
  184. void OnDrawGizmosSelected()
  185. {
  186. if (drawWireframeWhenSelectedOnly)
  187. DrawWireframe();
  188. }
  189. public void DrawWireframe()
  190. {
  191. if (vertices == null || vertices.Length == 0)
  192. return;
  193. var offset = transform.TransformVector(Vector3.up * wireframeHeight);
  194. for (int i = 0; i < 4; i++)
  195. {
  196. int next = (i + 1) % 4;
  197. var a = transform.TransformPoint(vertices[i]);
  198. var b = a + offset;
  199. var c = transform.TransformPoint(vertices[next]);
  200. var d = c + offset;
  201. Gizmos.DrawLine(a, b);
  202. Gizmos.DrawLine(a, c);
  203. Gizmos.DrawLine(b, d);
  204. }
  205. }
  206. public void OnEnable()
  207. {
  208. if (Application.isPlaying)
  209. {
  210. GetComponent<MeshRenderer>().enabled = drawInGame;
  211. // No need to remain enabled at runtime.
  212. // Anyone that wants to change properties at runtime
  213. // should call BuildMesh themselves.
  214. enabled = false;
  215. // If we want the configured bounds of the user,
  216. // we need to wait for tracking.
  217. if (drawInGame && size == Size.Calibrated)
  218. StartCoroutine(UpdateBounds());
  219. }
  220. }
  221. IEnumerator UpdateBounds()
  222. {
  223. GetComponent<MeshFilter>().mesh = null; // clear existing
  224. var chaperone = OpenVR.Chaperone;
  225. if (chaperone == null)
  226. yield break;
  227. while (chaperone.GetCalibrationState() != ChaperoneCalibrationState.OK)
  228. yield return null;
  229. BuildMesh();
  230. }
  231. }
  232. }