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