IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
SteamVR_Camera.cs
1 //======= Copyright (c) Valve Corporation, All rights reserved. ===============
2 //
3 // Purpose: Adds SteamVR render support to existing camera objects
4 //
5 //=============================================================================
6 
7 using UnityEngine;
8 using System.Collections;
9 using System.Reflection;
10 using Valve.VR;
11 
12 [RequireComponent(typeof(Camera))]
13 public class SteamVR_Camera : MonoBehaviour
14 {
15  [SerializeField]
16  private Transform _head;
17  public Transform head { get { return _head; } }
18  public Transform offset { get { return _head; } } // legacy
19  public Transform origin { get { return _head.parent; } }
20 
21  public new Camera camera { get; private set; }
22 
23  [SerializeField]
24  private Transform _ears;
25  public Transform ears { get { return _ears; } }
26 
27  public Ray GetRay()
28  {
29  return new Ray(_head.position, _head.forward);
30  }
31 
32  public bool wireframe = false;
33 
34  static public float sceneResolutionScale
35  {
36  get { return UnityEngine.XR.XRSettings.eyeTextureResolutionScale; }
37  set { UnityEngine.XR.XRSettings.eyeTextureResolutionScale = value; }
38  }
39 
40  #region Enable / Disable
41 
42  void OnDisable()
43  {
44  SteamVR_Render.Remove(this);
45  }
46 
47  void OnEnable()
48  {
49  // Bail if no hmd is connected
50  var vr = SteamVR.instance;
51  if (vr == null)
52  {
53  if (head != null)
54  {
55  head.GetComponent<SteamVR_TrackedObject>().enabled = false;
56  }
57 
58  enabled = false;
59  return;
60  }
61 
62  // Convert camera rig for native OpenVR integration.
63  var t = transform;
64  if (head != t)
65  {
66  Expand();
67 
68  t.parent = origin;
69 
70  while (head.childCount > 0)
71  head.GetChild(0).parent = t;
72 
73  // Keep the head around, but parent to the camera now since it moves with the hmd
74  // but existing content may still have references to this object.
75  head.parent = t;
76  head.localPosition = Vector3.zero;
77  head.localRotation = Quaternion.identity;
78  head.localScale = Vector3.one;
79  head.gameObject.SetActive(false);
80 
81  _head = t;
82  }
83 
84  if (ears == null)
85  {
86  var e = transform.GetComponentInChildren<SteamVR_Ears>();
87  if (e != null)
88  _ears = e.transform;
89  }
90 
91  if (ears != null)
92  ears.GetComponent<SteamVR_Ears>().vrcam = this;
93 
94  SteamVR_Render.Add(this);
95  }
96 
97  #endregion
98 
99  #region Functionality to ensure SteamVR_Camera component is always the last component on an object
100 
101  void Awake()
102  {
103  camera = GetComponent<Camera>(); // cached to avoid runtime lookup
104  ForceLast();
105  }
106 
107  static Hashtable values;
108 
109  public void ForceLast()
110  {
111  if (values != null)
112  {
113  // Restore values on new instance
114  foreach (DictionaryEntry entry in values)
115  {
116  var f = entry.Key as FieldInfo;
117  f.SetValue(this, entry.Value);
118  }
119  values = null;
120  }
121  else
122  {
123  // Make sure it's the last component
124  var components = GetComponents<Component>();
125 
126  // But first make sure there aren't any other SteamVR_Cameras on this object.
127  for (int i = 0; i < components.Length; i++)
128  {
129  var c = components[i] as SteamVR_Camera;
130  if (c != null && c != this)
131  {
132  DestroyImmediate(c);
133  }
134  }
135 
136  components = GetComponents<Component>();
137 
138  if (this != components[components.Length - 1])
139  {
140  // Store off values to be restored on new instance
141  values = new Hashtable();
142  var fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
143  foreach (var f in fields)
144  if (f.IsPublic || f.IsDefined(typeof(SerializeField), true))
145  values[f] = f.GetValue(this);
146 
147  var go = gameObject;
148  DestroyImmediate(this);
149  go.AddComponent<SteamVR_Camera>().ForceLast();
150  }
151  }
152  }
153 
154  #endregion
155 
156  #region Expand / Collapse object hierarchy
157 
158 #if UNITY_EDITOR
159  public bool isExpanded { get { return head != null && transform.parent == head; } }
160 #endif
161  const string eyeSuffix = " (eye)";
162  const string earsSuffix = " (ears)";
163  const string headSuffix = " (head)";
164  const string originSuffix = " (origin)";
165  public string baseName { get { return name.EndsWith(eyeSuffix) ? name.Substring(0, name.Length - eyeSuffix.Length) : name; } }
166 
167  // Object hierarchy creation to make it easy to parent other objects appropriately,
168  // otherwise this gets called on demand at runtime. Remaining initialization is
169  // performed at startup, once the hmd has been identified.
170  public void Expand()
171  {
172  var _origin = transform.parent;
173  if (_origin == null)
174  {
175  _origin = new GameObject(name + originSuffix).transform;
176  _origin.localPosition = transform.localPosition;
177  _origin.localRotation = transform.localRotation;
178  _origin.localScale = transform.localScale;
179  }
180 
181  if (head == null)
182  {
183  _head = new GameObject(name + headSuffix, typeof(SteamVR_TrackedObject)).transform;
184  head.parent = _origin;
185  head.position = transform.position;
186  head.rotation = transform.rotation;
187  head.localScale = Vector3.one;
188  head.tag = tag;
189  }
190 
191  if (transform.parent != head)
192  {
193  transform.parent = head;
194  transform.localPosition = Vector3.zero;
195  transform.localRotation = Quaternion.identity;
196  transform.localScale = Vector3.one;
197 
198  while (transform.childCount > 0)
199  transform.GetChild(0).parent = head;
200 #if !UNITY_2017_2_OR_NEWER
201  var guiLayer = GetComponent<GUILayer>();
202  if (guiLayer != null)
203  {
204  DestroyImmediate(guiLayer);
205  head.gameObject.AddComponent<GUILayer>();
206  }
207 #endif
208  var audioListener = GetComponent<AudioListener>();
209  if (audioListener != null)
210  {
211  DestroyImmediate(audioListener);
212  _ears = new GameObject(name + earsSuffix, typeof(SteamVR_Ears)).transform;
213  ears.parent = _head;
214  ears.localPosition = Vector3.zero;
215  ears.localRotation = Quaternion.identity;
216  ears.localScale = Vector3.one;
217  }
218  }
219 
220  if (!name.EndsWith(eyeSuffix))
221  name += eyeSuffix;
222  }
223 
224  public void Collapse()
225  {
226  transform.parent = null;
227 
228  // Move children and components from head back to camera.
229  while (head.childCount > 0)
230  head.GetChild(0).parent = transform;
231 #if !UNITY_2017_2_OR_NEWER
232  var guiLayer = head.GetComponent<GUILayer>();
233  if (guiLayer != null)
234  {
235  DestroyImmediate(guiLayer);
236  gameObject.AddComponent<GUILayer>();
237  }
238 #endif
239  if (ears != null)
240  {
241  while (ears.childCount > 0)
242  ears.GetChild(0).parent = transform;
243 
244  DestroyImmediate(ears.gameObject);
245  _ears = null;
246 
247  gameObject.AddComponent(typeof(AudioListener));
248  }
249 
250  if (origin != null)
251  {
252  // If we created the origin originally, destroy it now.
253  if (origin.name.EndsWith(originSuffix))
254  {
255  // Reparent any children so we don't accidentally delete them.
256  var _origin = origin;
257  while (_origin.childCount > 0)
258  _origin.GetChild(0).parent = _origin.parent;
259 
260  DestroyImmediate(_origin.gameObject);
261  }
262  else
263  {
264  transform.parent = origin;
265  }
266  }
267 
268  DestroyImmediate(head.gameObject);
269  _head = null;
270 
271  if (name.EndsWith(eyeSuffix))
272  name = name.Substring(0, name.Length - eyeSuffix.Length);
273  }
274 
275  #endregion
276 }
277