SteamVR
This commit is contained in:
Executable
+448
@@ -0,0 +1,448 @@
|
||||
//========= Copyright 2014, 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;
|
||||
|
||||
[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; } }
|
||||
|
||||
[SerializeField]
|
||||
private Transform _ears;
|
||||
public Transform ears { get { return _ears; } }
|
||||
|
||||
public Ray GetRay()
|
||||
{
|
||||
return new Ray(_head.position, _head.forward);
|
||||
}
|
||||
|
||||
public bool wireframe = false;
|
||||
|
||||
[SerializeField]
|
||||
private SteamVR_CameraFlip flip;
|
||||
|
||||
#region Materials
|
||||
|
||||
static public Material blitMaterial;
|
||||
|
||||
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
||||
// Using a single shared offscreen buffer to render the scene. This needs to be larger
|
||||
// than the backbuffer to account for distortion correction. The default resolution
|
||||
// gives us 1:1 sized pixels in the center of view, but quality can be adjusted up or
|
||||
// down using the following scale value to balance performance.
|
||||
static public float sceneResolutionScale = 1.0f;
|
||||
static private RenderTexture _sceneTexture;
|
||||
static public RenderTexture GetSceneTexture(bool hdr)
|
||||
{
|
||||
var vr = SteamVR.instance;
|
||||
if (vr == null)
|
||||
return null;
|
||||
|
||||
int w = (int)(vr.sceneWidth * sceneResolutionScale);
|
||||
int h = (int)(vr.sceneHeight * sceneResolutionScale);
|
||||
int aa = QualitySettings.antiAliasing == 0 ? 1 : QualitySettings.antiAliasing;
|
||||
var format = hdr ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
|
||||
|
||||
if (_sceneTexture != null)
|
||||
{
|
||||
if (_sceneTexture.width != w || _sceneTexture.height != h || _sceneTexture.antiAliasing != aa || _sceneTexture.format != format)
|
||||
{
|
||||
Debug.Log(string.Format("Recreating scene texture.. Old: {0}x{1} MSAA={2} [{3}] New: {4}x{5} MSAA={6} [{7}]",
|
||||
_sceneTexture.width, _sceneTexture.height, _sceneTexture.antiAliasing, _sceneTexture.format, w, h, aa, format));
|
||||
Object.Destroy(_sceneTexture);
|
||||
_sceneTexture = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_sceneTexture == null)
|
||||
{
|
||||
_sceneTexture = new RenderTexture(w, h, 0, format);
|
||||
_sceneTexture.antiAliasing = aa;
|
||||
|
||||
// OpenVR assumes floating point render targets are linear unless otherwise specified.
|
||||
var colorSpace = (hdr && QualitySettings.activeColorSpace == ColorSpace.Gamma) ? EColorSpace.Gamma : EColorSpace.Auto;
|
||||
SteamVR.Unity.SetColorSpace(colorSpace);
|
||||
}
|
||||
|
||||
return _sceneTexture;
|
||||
}
|
||||
#else
|
||||
static public float sceneResolutionScale
|
||||
{
|
||||
get { return UnityEngine.VR.VRSettings.renderScale; }
|
||||
set { UnityEngine.VR.VRSettings.renderScale = value; }
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#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_GameView>().enabled = false;
|
||||
head.GetComponent<SteamVR_TrackedObject>().enabled = false;
|
||||
}
|
||||
|
||||
if (flip != null)
|
||||
flip.enabled = false;
|
||||
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
#if !(UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
||||
// 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 (flip != null)
|
||||
{
|
||||
DestroyImmediate(flip);
|
||||
flip = null;
|
||||
}
|
||||
#else
|
||||
// Ensure rig is properly set up
|
||||
Expand();
|
||||
|
||||
if (blitMaterial == null)
|
||||
{
|
||||
blitMaterial = new Material(Shader.Find("Custom/SteamVR_Blit"));
|
||||
}
|
||||
|
||||
// Set remaining hmd specific settings
|
||||
var camera = GetComponent<Camera>();
|
||||
camera.fieldOfView = vr.fieldOfView;
|
||||
camera.aspect = vr.aspect;
|
||||
camera.eventMask = 0; // disable mouse events
|
||||
camera.orthographic = false; // force perspective
|
||||
camera.enabled = false; // manually rendered by SteamVR_Render
|
||||
|
||||
if (camera.actualRenderingPath != RenderingPath.Forward && QualitySettings.antiAliasing > 1)
|
||||
{
|
||||
Debug.LogWarning("MSAA only supported in Forward rendering path. (disabling MSAA)");
|
||||
QualitySettings.antiAliasing = 0;
|
||||
}
|
||||
|
||||
// Ensure game view camera hdr setting matches
|
||||
var headCam = head.GetComponent<Camera>();
|
||||
if (headCam != null)
|
||||
{
|
||||
headCam.hdr = camera.hdr;
|
||||
headCam.renderingPath = camera.renderingPath;
|
||||
}
|
||||
#endif
|
||||
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() { 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)
|
||||
{
|
||||
if (c.flip != null)
|
||||
DestroyImmediate(c.flip);
|
||||
DestroyImmediate(c);
|
||||
}
|
||||
}
|
||||
|
||||
components = GetComponents<Component>();
|
||||
|
||||
#if !(UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
||||
if (this != components[components.Length - 1])
|
||||
{
|
||||
#else
|
||||
if (this != components[components.Length - 1] || flip == null)
|
||||
{
|
||||
if (flip == null)
|
||||
flip = gameObject.AddComponent<SteamVR_CameraFlip>();
|
||||
#endif
|
||||
// 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_GameView), typeof(SteamVR_TrackedObject)).transform;
|
||||
head.parent = _origin;
|
||||
head.position = transform.position;
|
||||
head.rotation = transform.rotation;
|
||||
head.localScale = Vector3.one;
|
||||
head.tag = tag;
|
||||
|
||||
var camera = head.GetComponent<Camera>();
|
||||
camera.clearFlags = CameraClearFlags.Nothing;
|
||||
camera.cullingMask = 0;
|
||||
camera.eventMask = 0;
|
||||
camera.orthographic = true;
|
||||
camera.orthographicSize = 1;
|
||||
camera.nearClipPlane = 0;
|
||||
camera.farClipPlane = 1;
|
||||
camera.useOcclusionCulling = false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
var guiLayer = GetComponent<GUILayer>();
|
||||
if (guiLayer != null)
|
||||
{
|
||||
DestroyImmediate(guiLayer);
|
||||
head.gameObject.AddComponent<GUILayer>();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
var guiLayer = head.GetComponent<GUILayer>();
|
||||
if (guiLayer != null)
|
||||
{
|
||||
DestroyImmediate(guiLayer);
|
||||
gameObject.AddComponent<GUILayer>();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
||||
|
||||
#region Render callbacks
|
||||
|
||||
void OnPreRender()
|
||||
{
|
||||
if (flip)
|
||||
flip.enabled = (SteamVR_Render.Top() == this && SteamVR.instance.graphicsAPI == EGraphicsAPIConvention.API_DirectX);
|
||||
|
||||
var headCam = head.GetComponent<Camera>();
|
||||
if (headCam != null)
|
||||
headCam.enabled = (SteamVR_Render.Top() == this);
|
||||
|
||||
if (wireframe)
|
||||
GL.wireframe = true;
|
||||
}
|
||||
|
||||
void OnPostRender()
|
||||
{
|
||||
if (wireframe)
|
||||
GL.wireframe = false;
|
||||
}
|
||||
|
||||
void OnRenderImage(RenderTexture src, RenderTexture dest)
|
||||
{
|
||||
if (SteamVR_Render.Top() == this)
|
||||
{
|
||||
int eventID;
|
||||
if (SteamVR_Render.eye == EVREye.Eye_Left)
|
||||
{
|
||||
// Get gpu started on work early to avoid bubbles at the top of the frame.
|
||||
SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_Flush);
|
||||
|
||||
eventID = SteamVR.Unity.k_nRenderEventID_SubmitL;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventID = SteamVR.Unity.k_nRenderEventID_SubmitR;
|
||||
}
|
||||
|
||||
// Queue up a call on the render thread to Submit our render target to the compositor.
|
||||
SteamVR_Utils.QueueEventOnRenderThread(eventID);
|
||||
}
|
||||
|
||||
|
||||
Graphics.SetRenderTarget(dest);
|
||||
SteamVR_Camera.blitMaterial.mainTexture = src;
|
||||
|
||||
GL.PushMatrix();
|
||||
GL.LoadOrtho();
|
||||
SteamVR_Camera.blitMaterial.SetPass(0);
|
||||
GL.Begin(GL.QUADS);
|
||||
GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(-1, 1, 0);
|
||||
GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(1, 1, 0);
|
||||
GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(1, -1, 0);
|
||||
GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(-1, -1, 0);
|
||||
GL.End();
|
||||
GL.PopMatrix();
|
||||
|
||||
Graphics.SetRenderTarget(null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user