436 lines
11 KiB
C#
Executable File
436 lines
11 KiB
C#
Executable File
//========= Copyright 2014, Valve Corporation, All rights reserved. ===========
|
|
//
|
|
// Purpose: Handles rendering of all SteamVR_Cameras
|
|
//
|
|
//=============================================================================
|
|
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using Valve.VR;
|
|
|
|
public class SteamVR_Render : MonoBehaviour
|
|
{
|
|
public bool pauseGameWhenDashboardIsVisible = true;
|
|
public bool lockPhysicsUpdateRateToRenderFrequency = true;
|
|
|
|
public SteamVR_ExternalCamera externalCamera;
|
|
public string externalCameraConfigPath = "externalcamera.cfg";
|
|
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
public LayerMask leftMask, rightMask;
|
|
|
|
SteamVR_CameraMask cameraMask;
|
|
#endif
|
|
public ETrackingUniverseOrigin trackingSpace = ETrackingUniverseOrigin.TrackingUniverseStanding;
|
|
|
|
static public EVREye eye { get; private set; }
|
|
|
|
static private SteamVR_Render _instance;
|
|
static public SteamVR_Render instance
|
|
{
|
|
get
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
_instance = GameObject.FindObjectOfType<SteamVR_Render>();
|
|
|
|
if (_instance == null)
|
|
_instance = new GameObject("[SteamVR]").AddComponent<SteamVR_Render>();
|
|
}
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
_instance = null;
|
|
}
|
|
|
|
static private bool isQuitting;
|
|
void OnApplicationQuit()
|
|
{
|
|
isQuitting = true;
|
|
SteamVR.SafeDispose();
|
|
}
|
|
|
|
static public void Add(SteamVR_Camera vrcam)
|
|
{
|
|
if (!isQuitting)
|
|
instance.AddInternal(vrcam);
|
|
}
|
|
|
|
static public void Remove(SteamVR_Camera vrcam)
|
|
{
|
|
if (!isQuitting && _instance != null)
|
|
instance.RemoveInternal(vrcam);
|
|
}
|
|
|
|
static public SteamVR_Camera Top()
|
|
{
|
|
if (!isQuitting)
|
|
return instance.TopInternal();
|
|
|
|
return null;
|
|
}
|
|
|
|
private SteamVR_Camera[] cameras = new SteamVR_Camera[0];
|
|
|
|
void AddInternal(SteamVR_Camera vrcam)
|
|
{
|
|
var camera = vrcam.GetComponent<Camera>();
|
|
var length = cameras.Length;
|
|
var sorted = new SteamVR_Camera[length + 1];
|
|
int insert = 0;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
var c = cameras[i].GetComponent<Camera>();
|
|
if (i == insert && c.depth > camera.depth)
|
|
sorted[insert++] = vrcam;
|
|
|
|
sorted[insert++] = cameras[i];
|
|
}
|
|
if (insert == length)
|
|
sorted[insert] = vrcam;
|
|
|
|
cameras = sorted;
|
|
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
enabled = true;
|
|
#endif
|
|
}
|
|
|
|
void RemoveInternal(SteamVR_Camera vrcam)
|
|
{
|
|
var length = cameras.Length;
|
|
int count = 0;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
var c = cameras[i];
|
|
if (c == vrcam)
|
|
++count;
|
|
}
|
|
if (count == 0)
|
|
return;
|
|
|
|
var sorted = new SteamVR_Camera[length - count];
|
|
int insert = 0;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
var c = cameras[i];
|
|
if (c != vrcam)
|
|
sorted[insert++] = c;
|
|
}
|
|
|
|
cameras = sorted;
|
|
}
|
|
|
|
SteamVR_Camera TopInternal()
|
|
{
|
|
if (cameras.Length > 0)
|
|
return cameras[cameras.Length - 1];
|
|
|
|
return null;
|
|
}
|
|
|
|
public TrackedDevicePose_t[] poses = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount];
|
|
public TrackedDevicePose_t[] gamePoses = new TrackedDevicePose_t[0];
|
|
|
|
static private bool _pauseRendering;
|
|
static public bool pauseRendering
|
|
{
|
|
get { return _pauseRendering; }
|
|
set
|
|
{
|
|
_pauseRendering = value;
|
|
#if !(UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
var compositor = OpenVR.Compositor;
|
|
if (compositor != null)
|
|
compositor.SuspendRendering(value);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private IEnumerator RenderLoop()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return new WaitForEndOfFrame();
|
|
|
|
if (pauseRendering)
|
|
continue;
|
|
|
|
var compositor = OpenVR.Compositor;
|
|
if (compositor != null)
|
|
{
|
|
if (!compositor.CanRenderScene())
|
|
continue;
|
|
|
|
compositor.SetTrackingSpace(trackingSpace);
|
|
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_WaitGetPoses);
|
|
|
|
// Hack to flush render event that was queued in Update (this ensures WaitGetPoses has returned before we grab the new values).
|
|
SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - Begin");
|
|
SteamVR_Camera.GetSceneTexture(cameras[0].GetComponent<Camera>().hdr).GetNativeTexturePtr();
|
|
SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - End");
|
|
|
|
compositor.GetLastPoses(poses, gamePoses);
|
|
SteamVR_Utils.Event.Send("new_poses", poses);
|
|
SteamVR_Utils.Event.Send("new_poses_applied");
|
|
#endif
|
|
}
|
|
|
|
var overlay = SteamVR_Overlay.instance;
|
|
if (overlay != null)
|
|
overlay.UpdateOverlay();
|
|
|
|
RenderExternalCamera();
|
|
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
var vr = SteamVR.instance;
|
|
RenderEye(vr, EVREye.Eye_Left);
|
|
RenderEye(vr, EVREye.Eye_Right);
|
|
|
|
// Move cameras back to head position so they can be tracked reliably
|
|
foreach (var c in cameras)
|
|
{
|
|
c.transform.localPosition = Vector3.zero;
|
|
c.transform.localRotation = Quaternion.identity;
|
|
}
|
|
|
|
if (cameraMask != null)
|
|
cameraMask.Clear();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
void RenderEye(SteamVR vr, EVREye eye)
|
|
{
|
|
int i = (int)eye;
|
|
SteamVR_Render.eye = eye;
|
|
|
|
if (cameraMask != null)
|
|
cameraMask.Set(vr, eye);
|
|
|
|
foreach (var c in cameras)
|
|
{
|
|
c.transform.localPosition = vr.eyes[i].pos;
|
|
c.transform.localRotation = vr.eyes[i].rot;
|
|
|
|
// Update position to keep from getting culled
|
|
//cameraMask.transform.position = c.transform.position;
|
|
|
|
var camera = c.GetComponent<Camera>();
|
|
camera.targetTexture = SteamVR_Camera.GetSceneTexture(camera.hdr);
|
|
int cullingMask = camera.cullingMask;
|
|
if (eye == EVREye.Eye_Left)
|
|
{
|
|
camera.cullingMask &= ~rightMask;
|
|
camera.cullingMask |= leftMask;
|
|
}
|
|
else
|
|
{
|
|
camera.cullingMask &= ~leftMask;
|
|
camera.cullingMask |= rightMask;
|
|
}
|
|
camera.Render();
|
|
camera.cullingMask = cullingMask;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void RenderExternalCamera()
|
|
{
|
|
if (externalCamera == null)
|
|
return;
|
|
|
|
if (!externalCamera.gameObject.activeInHierarchy)
|
|
return;
|
|
|
|
var frameSkip = (int)Mathf.Max(externalCamera.config.frameSkip, 0.0f);
|
|
if (Time.frameCount % (frameSkip + 1) != 0)
|
|
return;
|
|
|
|
// Keep external camera relative to the most relevant vr camera.
|
|
externalCamera.AttachToCamera(TopInternal());
|
|
|
|
externalCamera.RenderNear();
|
|
externalCamera.RenderFar();
|
|
}
|
|
|
|
float sceneResolutionScale = 1.0f, timeScale = 1.0f;
|
|
|
|
private void OnInputFocus(params object[] args)
|
|
{
|
|
bool hasFocus = (bool)args[0];
|
|
if (hasFocus)
|
|
{
|
|
if (pauseGameWhenDashboardIsVisible)
|
|
{
|
|
Time.timeScale = timeScale;
|
|
}
|
|
|
|
SteamVR_Camera.sceneResolutionScale = sceneResolutionScale;
|
|
}
|
|
else
|
|
{
|
|
if (pauseGameWhenDashboardIsVisible)
|
|
{
|
|
timeScale = Time.timeScale;
|
|
Time.timeScale = 0.0f;
|
|
}
|
|
|
|
sceneResolutionScale = SteamVR_Camera.sceneResolutionScale;
|
|
SteamVR_Camera.sceneResolutionScale = 0.5f;
|
|
}
|
|
}
|
|
|
|
void OnQuit(params object[] args)
|
|
{
|
|
#if UNITY_EDITOR
|
|
foreach (System.Reflection.Assembly a in System.AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
var t = a.GetType("UnityEditor.EditorApplication");
|
|
if (t != null)
|
|
{
|
|
t.GetProperty("isPlaying").SetValue(null, false, null);
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
Application.Quit();
|
|
#endif
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
StartCoroutine("RenderLoop");
|
|
SteamVR_Utils.Event.Listen("input_focus", OnInputFocus);
|
|
SteamVR_Utils.Event.Listen("Quit", OnQuit);
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
StopAllCoroutines();
|
|
SteamVR_Utils.Event.Remove("input_focus", OnInputFocus);
|
|
SteamVR_Utils.Event.Remove("Quit", OnQuit);
|
|
}
|
|
|
|
void Awake()
|
|
{
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
//var go = new GameObject("cameraMask");
|
|
//go.transform.parent = transform;
|
|
//cameraMask = go.AddComponent<SteamVR_CameraMask>();
|
|
#endif
|
|
if (externalCamera == null && System.IO.File.Exists(externalCameraConfigPath))
|
|
{
|
|
var prefab = Resources.Load<GameObject>("SteamVR_ExternalCamera");
|
|
var instance = Instantiate(prefab);
|
|
instance.gameObject.name = "External Camera";
|
|
|
|
externalCamera = instance.transform.GetChild(0).GetComponent<SteamVR_ExternalCamera>();
|
|
externalCamera.configPath = externalCameraConfigPath;
|
|
externalCamera.ReadConfig();
|
|
}
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
// We want to call this as soon after Present as possible.
|
|
SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_PostPresentHandoff);
|
|
#endif
|
|
}
|
|
|
|
#if !(UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
private SteamVR_UpdatePoses poseUpdater;
|
|
#endif
|
|
|
|
void Update()
|
|
{
|
|
#if !(UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0)
|
|
if (poseUpdater == null)
|
|
{
|
|
var go = new GameObject("poseUpdater");
|
|
go.transform.parent = transform;
|
|
poseUpdater = go.AddComponent<SteamVR_UpdatePoses>();
|
|
}
|
|
#else
|
|
if (cameras.Length == 0)
|
|
{
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
// If our FixedUpdate rate doesn't match our render framerate, then catch the handoff here.
|
|
SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_PostPresentHandoff);
|
|
#endif
|
|
// Force controller update in case no one else called this frame to ensure prevState gets updated.
|
|
SteamVR_Controller.Update();
|
|
|
|
// Dispatch any OpenVR events.
|
|
var system = OpenVR.System;
|
|
if (system != null)
|
|
{
|
|
var vrEvent = new VREvent_t();
|
|
var size = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VREvent_t));
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
if (!system.PollNextEvent(ref vrEvent, size))
|
|
break;
|
|
|
|
switch ((EVREventType)vrEvent.eventType)
|
|
{
|
|
case EVREventType.VREvent_InputFocusCaptured: // another app has taken focus (likely dashboard)
|
|
if (vrEvent.data.process.oldPid == 0)
|
|
{
|
|
SteamVR_Utils.Event.Send("input_focus", false);
|
|
}
|
|
break;
|
|
case EVREventType.VREvent_InputFocusReleased: // that app has released input focus
|
|
if (vrEvent.data.process.pid == 0)
|
|
{
|
|
SteamVR_Utils.Event.Send("input_focus", true);
|
|
}
|
|
break;
|
|
case EVREventType.VREvent_ShowRenderModels:
|
|
SteamVR_Utils.Event.Send("hide_render_models", false);
|
|
break;
|
|
case EVREventType.VREvent_HideRenderModels:
|
|
SteamVR_Utils.Event.Send("hide_render_models", true);
|
|
break;
|
|
default:
|
|
var name = System.Enum.GetName(typeof(EVREventType), vrEvent.eventType);
|
|
if (name != null)
|
|
SteamVR_Utils.Event.Send(name.Substring(8) /*strip VREvent_*/, vrEvent);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure various settings to minimize latency.
|
|
Application.targetFrameRate = -1;
|
|
Application.runInBackground = true; // don't require companion window focus
|
|
QualitySettings.maxQueuedFrames = -1;
|
|
QualitySettings.vSyncCount = 0; // this applies to the companion window
|
|
|
|
if (lockPhysicsUpdateRateToRenderFrequency && Time.timeScale > 0.0f)
|
|
{
|
|
var vr = SteamVR.instance;
|
|
if (vr != null)
|
|
{
|
|
var timing = new Compositor_FrameTiming();
|
|
timing.m_nSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(Compositor_FrameTiming));
|
|
vr.compositor.GetFrameTiming(ref timing, 0);
|
|
|
|
Time.fixedDeltaTime = Time.timeScale / vr.hmd_DisplayFrequency;
|
|
Time.maximumDeltaTime = Time.fixedDeltaTime * timing.m_nNumFramePresents;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|