campfire/Assets/SteamVR/Scripts/SteamVR_ExternalCamera.cs
2020-04-29 20:40:05 +03:00

432 lines
15 KiB
C#

//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: Used to render an external camera of vr player (split front/back).
//
//=============================================================================
using UnityEngine;
using UnityEngine.Rendering;
using Valve.VR;
namespace Valve.VR
{
public class SteamVR_ExternalCamera : MonoBehaviour
{
private SteamVR_Action_Pose cameraPose = null;
private SteamVR_Input_Sources cameraInputSource = SteamVR_Input_Sources.Camera;
[System.Serializable]
public struct Config
{
public float x, y, z;
public float rx, ry, rz;
public float fov;
public float near, far;
public float sceneResolutionScale;
public float frameSkip;
public float nearOffset, farOffset;
public float hmdOffset;
public float r, g, b, a; // chroma key override
public bool disableStandardAssets;
}
[Space()]
public Config config;
public string configPath;
[Tooltip("This will automatically activate the action set the specified pose belongs to. And deactivate it when this component is disabled.")]
public bool autoEnableDisableActionSet = true;
public void ReadConfig()
{
try
{
var mCam = new HmdMatrix34_t();
var readCamMatrix = false;
object c = config; // box
var lines = System.IO.File.ReadAllLines(configPath);
foreach (var line in lines)
{
var split = line.Split('=');
if (split.Length == 2)
{
var key = split[0];
if (key == "m")
{
var values = split[1].Split(',');
if (values.Length == 12)
{
mCam.m0 = float.Parse(values[0]);
mCam.m1 = float.Parse(values[1]);
mCam.m2 = float.Parse(values[2]);
mCam.m3 = float.Parse(values[3]);
mCam.m4 = float.Parse(values[4]);
mCam.m5 = float.Parse(values[5]);
mCam.m6 = float.Parse(values[6]);
mCam.m7 = float.Parse(values[7]);
mCam.m8 = float.Parse(values[8]);
mCam.m9 = float.Parse(values[9]);
mCam.m10 = float.Parse(values[10]);
mCam.m11 = float.Parse(values[11]);
readCamMatrix = true;
}
}
#if !UNITY_METRO
else if (key == "disableStandardAssets")
{
var field = c.GetType().GetField(key);
if (field != null)
field.SetValue(c, bool.Parse(split[1]));
}
else
{
var field = c.GetType().GetField(key);
if (field != null)
field.SetValue(c, float.Parse(split[1]));
}
#endif
}
}
config = (Config)c; //unbox
// Convert calibrated camera matrix settings.
if (readCamMatrix)
{
var t = new SteamVR_Utils.RigidTransform(mCam);
config.x = t.pos.x;
config.y = t.pos.y;
config.z = t.pos.z;
var angles = t.rot.eulerAngles;
config.rx = angles.x;
config.ry = angles.y;
config.rz = angles.z;
}
}
catch { }
// Clear target so AttachToCamera gets called to pick up any changes.
target = null;
#if !UNITY_METRO
// Listen for changes.
if (watcher == null)
{
var fi = new System.IO.FileInfo(configPath);
watcher = new System.IO.FileSystemWatcher(fi.DirectoryName, fi.Name);
watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite;
watcher.Changed += new System.IO.FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
}
public void SetupPose(SteamVR_Action_Pose newCameraPose, SteamVR_Input_Sources newCameraSource)
{
cameraPose = newCameraPose;
cameraInputSource = newCameraSource;
AutoEnableActionSet();
SteamVR_Behaviour_Pose poseBehaviour = this.gameObject.AddComponent<SteamVR_Behaviour_Pose>();
poseBehaviour.poseAction = newCameraPose;
poseBehaviour.inputSource = newCameraSource;
}
public void SetupDeviceIndex(int deviceIndex)
{
SteamVR_TrackedObject trackedObject = this.gameObject.AddComponent<SteamVR_TrackedObject>();
trackedObject.SetDeviceIndex(deviceIndex);
}
void OnChanged(object source, System.IO.FileSystemEventArgs e)
{
ReadConfig();
}
System.IO.FileSystemWatcher watcher;
#else
}
#endif
Camera cam;
Transform target;
GameObject clipQuad;
Material clipMaterial;
protected SteamVR_ActionSet activatedActionSet;
protected SteamVR_Input_Sources activatedInputSource;
public void AttachToCamera(SteamVR_Camera steamVR_Camera)
{
Camera vrcam;
if (steamVR_Camera == null)
{
vrcam = Camera.main;
if (target == vrcam.transform)
return;
target = vrcam.transform;
}
else
{
vrcam = steamVR_Camera.camera;
if (target == steamVR_Camera.head)
return;
target = steamVR_Camera.head;
}
var root = transform.parent;
var origin = target.parent;
root.parent = origin;
root.localPosition = Vector3.zero;
root.localRotation = Quaternion.identity;
root.localScale = Vector3.one;
// Make a copy of the eye camera to pick up any camera fx.
vrcam.enabled = false;
var go = Instantiate(vrcam.gameObject);
vrcam.enabled = true;
go.name = "camera";
DestroyImmediate(go.GetComponent<SteamVR_Camera>());
DestroyImmediate(go.GetComponent<SteamVR_Fade>());
cam = go.GetComponent<Camera>();
cam.stereoTargetEye = StereoTargetEyeMask.None;
cam.fieldOfView = config.fov;
cam.useOcclusionCulling = false;
cam.enabled = false; // manually rendered
cam.rect = new Rect(0, 0, 1, 1); //fix order of operations issue
colorMat = new Material(Shader.Find("Custom/SteamVR_ColorOut"));
alphaMat = new Material(Shader.Find("Custom/SteamVR_AlphaOut"));
clipMaterial = new Material(Shader.Find("Custom/SteamVR_ClearAll"));
var offset = go.transform;
offset.parent = transform;
offset.localPosition = new Vector3(config.x, config.y, config.z);
offset.localRotation = Quaternion.Euler(config.rx, config.ry, config.rz);
offset.localScale = Vector3.one;
// Strip children of cloned object (AudioListener in particular).
while (offset.childCount > 0)
DestroyImmediate(offset.GetChild(0).gameObject);
// Setup clipping quad (using camera clip causes problems with shadows).
clipQuad = GameObject.CreatePrimitive(PrimitiveType.Quad);
clipQuad.name = "ClipQuad";
DestroyImmediate(clipQuad.GetComponent<MeshCollider>());
var clipRenderer = clipQuad.GetComponent<MeshRenderer>();
clipRenderer.material = clipMaterial;
clipRenderer.shadowCastingMode = ShadowCastingMode.Off;
clipRenderer.receiveShadows = false;
clipRenderer.lightProbeUsage = LightProbeUsage.Off;
clipRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
var clipTransform = clipQuad.transform;
clipTransform.parent = offset;
clipTransform.localScale = new Vector3(1000.0f, 1000.0f, 1.0f);
clipTransform.localRotation = Quaternion.identity;
clipQuad.SetActive(false);
}
public float GetTargetDistance()
{
if (target == null)
return config.near + 0.01f;
var offset = cam.transform;
var forward = new Vector3(offset.forward.x, 0.0f, offset.forward.z).normalized;
var targetPos = target.position + new Vector3(target.forward.x, 0.0f, target.forward.z).normalized * config.hmdOffset;
var distance = -(new Plane(forward, targetPos)).GetDistanceToPoint(offset.position);
return Mathf.Clamp(distance, config.near + 0.01f, config.far - 0.01f);
}
Material colorMat, alphaMat;
public void RenderNear()
{
var w = Screen.width / 2;
var h = Screen.height / 2;
if (cam.targetTexture == null || cam.targetTexture.width != w || cam.targetTexture.height != h)
{
var tex = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32);
tex.antiAliasing = QualitySettings.antiAliasing == 0 ? 1 : QualitySettings.antiAliasing;
cam.targetTexture = tex;
}
cam.nearClipPlane = config.near;
cam.farClipPlane = config.far;
var clearFlags = cam.clearFlags;
var backgroundColor = cam.backgroundColor;
cam.clearFlags = CameraClearFlags.Color;
cam.backgroundColor = Color.clear;
clipMaterial.color = new Color(config.r, config.g, config.b, config.a);
float dist = Mathf.Clamp(GetTargetDistance() + config.nearOffset, config.near, config.far);
var clipParent = clipQuad.transform.parent;
clipQuad.transform.position = clipParent.position + clipParent.forward * dist;
MonoBehaviour[] behaviours = null;
bool[] wasEnabled = null;
if (config.disableStandardAssets)
{
behaviours = cam.gameObject.GetComponents<MonoBehaviour>();
wasEnabled = new bool[behaviours.Length];
for (int i = 0; i < behaviours.Length; i++)
{
var behaviour = behaviours[i];
if (behaviour.enabled && behaviour.GetType().ToString().StartsWith("UnityStandardAssets."))
{
behaviour.enabled = false;
wasEnabled[i] = true;
}
}
}
clipQuad.SetActive(true);
cam.Render();
Graphics.DrawTexture(new Rect(0, 0, w, h), cam.targetTexture, colorMat);
// Re-render scene with post-processing fx disabled (if necessary) since they override alpha.
var pp = cam.gameObject.GetComponent("PostProcessingBehaviour") as MonoBehaviour;
if ((pp != null) && pp.enabled)
{
pp.enabled = false;
cam.Render();
pp.enabled = true;
}
Graphics.DrawTexture(new Rect(w, 0, w, h), cam.targetTexture, alphaMat);
// Restore settings.
clipQuad.SetActive(false);
if (behaviours != null)
{
for (int i = 0; i < behaviours.Length; i++)
{
if (wasEnabled[i])
{
behaviours[i].enabled = true;
}
}
}
cam.clearFlags = clearFlags;
cam.backgroundColor = backgroundColor;
}
public void RenderFar()
{
cam.nearClipPlane = config.near;
cam.farClipPlane = config.far;
cam.Render();
var w = Screen.width / 2;
var h = Screen.height / 2;
Graphics.DrawTexture(new Rect(0, h, w, h), cam.targetTexture, colorMat);
}
void OnGUI()
{
// Necessary for Graphics.DrawTexture to work even though we don't do anything here.
}
Camera[] cameras;
Rect[] cameraRects;
float sceneResolutionScale;
void OnEnable()
{
// Move game view cameras to lower-right quadrant.
cameras = FindObjectsOfType<Camera>();
if (cameras != null)
{
var numCameras = cameras.Length;
cameraRects = new Rect[numCameras];
for (int i = 0; i < numCameras; i++)
{
var cam = cameras[i];
cameraRects[i] = cam.rect;
if (cam == this.cam)
continue;
if (cam.targetTexture != null)
continue;
if (cam.GetComponent<SteamVR_Camera>() != null)
continue;
cam.rect = new Rect(0.5f, 0.0f, 0.5f, 0.5f);
}
}
if (config.sceneResolutionScale > 0.0f)
{
sceneResolutionScale = SteamVR_Camera.sceneResolutionScale;
SteamVR_Camera.sceneResolutionScale = config.sceneResolutionScale;
}
AutoEnableActionSet();
}
private void AutoEnableActionSet()
{
if (autoEnableDisableActionSet)
{
if (cameraPose != null)
{
if (cameraPose.actionSet.IsActive(cameraInputSource) == false)
{
activatedActionSet = cameraPose.actionSet; //automatically activate the actionset if it isn't active already. (will deactivate on component disable)
activatedInputSource = cameraInputSource;
cameraPose.actionSet.Activate(cameraInputSource);
}
}
}
}
void OnDisable()
{
if (autoEnableDisableActionSet)
{
if (activatedActionSet != null) //deactivate the action set we activated for this camera
{
activatedActionSet.Deactivate(activatedInputSource);
activatedActionSet = null;
}
}
// Restore game view cameras.
if (cameras != null)
{
var numCameras = cameras.Length;
for (int i = 0; i < numCameras; i++)
{
var cam = cameras[i];
if (cam != null)
cam.rect = cameraRects[i];
}
cameras = null;
cameraRects = null;
}
if (config.sceneResolutionScale > 0.0f)
{
SteamVR_Camera.sceneResolutionScale = sceneResolutionScale;
}
}
}
}