294 lines
9.4 KiB
C#
294 lines
9.4 KiB
C#
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
|
|
//
|
|
// Purpose: Adds SteamVR render support to existing camera objects
|
|
//
|
|
//=============================================================================
|
|
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Reflection;
|
|
using Valve.VR;
|
|
|
|
#if UNITY_2017_2_OR_NEWER
|
|
using UnityEngine.XR;
|
|
#else
|
|
using XRSettings = UnityEngine.VR.VRSettings;
|
|
using XRDevice = UnityEngine.VR.VRDevice;
|
|
#endif
|
|
|
|
namespace Valve.VR
|
|
{
|
|
[RequireComponent(typeof(Camera))]
|
|
public class SteamVR_Camera : MonoBehaviour
|
|
{
|
|
[SerializeField]
|
|
private Transform _head;
|
|
public Transform head { get { return _head; } }
|
|
public Transform offset { get { return _head; } } // legacy
|
|
public Transform origin { get { return _head.parent; } }
|
|
|
|
public new Camera camera { get; private set; }
|
|
|
|
[SerializeField]
|
|
private Transform _ears;
|
|
public Transform ears { get { return _ears; } }
|
|
|
|
public Ray GetRay()
|
|
{
|
|
return new Ray(_head.position, _head.forward);
|
|
}
|
|
|
|
public bool wireframe = false;
|
|
|
|
#if UNITY_2017_2_OR_NEWER
|
|
static public float sceneResolutionScale
|
|
{
|
|
get { return XRSettings.eyeTextureResolutionScale; }
|
|
set { XRSettings.eyeTextureResolutionScale = value; }
|
|
}
|
|
#else
|
|
static public float sceneResolutionScale
|
|
{
|
|
get { return XRSettings.renderScale; }
|
|
set { if (value == 0) return; XRSettings.renderScale = value; }
|
|
}
|
|
#endif
|
|
|
|
#region Enable / Disable
|
|
|
|
void OnDisable()
|
|
{
|
|
SteamVR_Render.Remove(this);
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
// Bail if no hmd is connected
|
|
var vr = SteamVR.instance;
|
|
if (vr == null)
|
|
{
|
|
if (head != null)
|
|
{
|
|
head.GetComponent<SteamVR_TrackedObject>().enabled = false;
|
|
}
|
|
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
// Convert camera rig for native OpenVR integration.
|
|
var t = transform;
|
|
if (head != t)
|
|
{
|
|
Expand();
|
|
|
|
t.parent = origin;
|
|
|
|
while (head.childCount > 0)
|
|
head.GetChild(0).parent = t;
|
|
|
|
// Keep the head around, but parent to the camera now since it moves with the hmd
|
|
// but existing content may still have references to this object.
|
|
head.parent = t;
|
|
head.localPosition = Vector3.zero;
|
|
head.localRotation = Quaternion.identity;
|
|
head.localScale = Vector3.one;
|
|
head.gameObject.SetActive(false);
|
|
|
|
_head = t;
|
|
}
|
|
|
|
if (ears == null)
|
|
{
|
|
var e = transform.GetComponentInChildren<SteamVR_Ears>();
|
|
if (e != null)
|
|
_ears = e.transform;
|
|
}
|
|
|
|
if (ears != null)
|
|
ears.GetComponent<SteamVR_Ears>().vrcam = this;
|
|
|
|
SteamVR_Render.Add(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Functionality to ensure SteamVR_Camera component is always the last component on an object
|
|
|
|
void Awake()
|
|
{
|
|
camera = GetComponent<Camera>(); // cached to avoid runtime lookup
|
|
ForceLast();
|
|
}
|
|
|
|
static Hashtable values;
|
|
|
|
public void ForceLast()
|
|
{
|
|
if (values != null)
|
|
{
|
|
// Restore values on new instance
|
|
foreach (DictionaryEntry entry in values)
|
|
{
|
|
var f = entry.Key as FieldInfo;
|
|
f.SetValue(this, entry.Value);
|
|
}
|
|
values = null;
|
|
}
|
|
else
|
|
{
|
|
// Make sure it's the last component
|
|
var components = GetComponents<Component>();
|
|
|
|
// But first make sure there aren't any other SteamVR_Cameras on this object.
|
|
for (int i = 0; i < components.Length; i++)
|
|
{
|
|
var c = components[i] as SteamVR_Camera;
|
|
if (c != null && c != this)
|
|
{
|
|
DestroyImmediate(c);
|
|
}
|
|
}
|
|
|
|
components = GetComponents<Component>();
|
|
|
|
if (this != components[components.Length - 1])
|
|
{
|
|
// Store off values to be restored on new instance
|
|
values = new Hashtable();
|
|
var fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
foreach (var f in fields)
|
|
if (f.IsPublic || f.IsDefined(typeof(SerializeField), true))
|
|
values[f] = f.GetValue(this);
|
|
|
|
var go = gameObject;
|
|
DestroyImmediate(this);
|
|
go.AddComponent<SteamVR_Camera>().ForceLast();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Expand / Collapse object hierarchy
|
|
|
|
#if UNITY_EDITOR
|
|
public bool isExpanded { get { return head != null && transform.parent == head; } }
|
|
#endif
|
|
const string eyeSuffix = " (eye)";
|
|
const string earsSuffix = " (ears)";
|
|
const string headSuffix = " (head)";
|
|
const string originSuffix = " (origin)";
|
|
public string baseName { get { return name.EndsWith(eyeSuffix) ? name.Substring(0, name.Length - eyeSuffix.Length) : name; } }
|
|
|
|
// Object hierarchy creation to make it easy to parent other objects appropriately,
|
|
// otherwise this gets called on demand at runtime. Remaining initialization is
|
|
// performed at startup, once the hmd has been identified.
|
|
public void Expand()
|
|
{
|
|
var _origin = transform.parent;
|
|
if (_origin == null)
|
|
{
|
|
_origin = new GameObject(name + originSuffix).transform;
|
|
_origin.localPosition = transform.localPosition;
|
|
_origin.localRotation = transform.localRotation;
|
|
_origin.localScale = transform.localScale;
|
|
}
|
|
|
|
if (head == null)
|
|
{
|
|
_head = new GameObject(name + headSuffix, typeof(SteamVR_TrackedObject)).transform;
|
|
head.parent = _origin;
|
|
head.position = transform.position;
|
|
head.rotation = transform.rotation;
|
|
head.localScale = Vector3.one;
|
|
head.tag = tag;
|
|
}
|
|
|
|
if (transform.parent != head)
|
|
{
|
|
transform.parent = head;
|
|
transform.localPosition = Vector3.zero;
|
|
transform.localRotation = Quaternion.identity;
|
|
transform.localScale = Vector3.one;
|
|
|
|
while (transform.childCount > 0)
|
|
transform.GetChild(0).parent = head;
|
|
#if !UNITY_2017_2_OR_NEWER
|
|
var guiLayer = GetComponent<GUILayer>();
|
|
if (guiLayer != null)
|
|
{
|
|
DestroyImmediate(guiLayer);
|
|
head.gameObject.AddComponent<GUILayer>();
|
|
}
|
|
#endif
|
|
var audioListener = GetComponent<AudioListener>();
|
|
if (audioListener != null)
|
|
{
|
|
DestroyImmediate(audioListener);
|
|
_ears = new GameObject(name + earsSuffix, typeof(SteamVR_Ears)).transform;
|
|
ears.parent = _head;
|
|
ears.localPosition = Vector3.zero;
|
|
ears.localRotation = Quaternion.identity;
|
|
ears.localScale = Vector3.one;
|
|
}
|
|
}
|
|
|
|
if (!name.EndsWith(eyeSuffix))
|
|
name += eyeSuffix;
|
|
}
|
|
|
|
public void Collapse()
|
|
{
|
|
transform.parent = null;
|
|
|
|
// Move children and components from head back to camera.
|
|
while (head.childCount > 0)
|
|
head.GetChild(0).parent = transform;
|
|
#if !UNITY_2017_2_OR_NEWER
|
|
var guiLayer = head.GetComponent<GUILayer>();
|
|
if (guiLayer != null)
|
|
{
|
|
DestroyImmediate(guiLayer);
|
|
gameObject.AddComponent<GUILayer>();
|
|
}
|
|
#endif
|
|
if (ears != null)
|
|
{
|
|
while (ears.childCount > 0)
|
|
ears.GetChild(0).parent = transform;
|
|
|
|
DestroyImmediate(ears.gameObject);
|
|
_ears = null;
|
|
|
|
gameObject.AddComponent(typeof(AudioListener));
|
|
}
|
|
|
|
if (origin != null)
|
|
{
|
|
// If we created the origin originally, destroy it now.
|
|
if (origin.name.EndsWith(originSuffix))
|
|
{
|
|
// Reparent any children so we don't accidentally delete them.
|
|
var _origin = origin;
|
|
while (_origin.childCount > 0)
|
|
_origin.GetChild(0).parent = _origin.parent;
|
|
|
|
DestroyImmediate(_origin.gameObject);
|
|
}
|
|
else
|
|
{
|
|
transform.parent = origin;
|
|
}
|
|
}
|
|
|
|
DestroyImmediate(head.gameObject);
|
|
_head = null;
|
|
|
|
if (name.EndsWith(eyeSuffix))
|
|
name = name.Substring(0, name.Length - eyeSuffix.Length);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |