//======= Copyright (c) Valve Corporation, All rights reserved. =============== // // Purpose: Generates a mesh based on field of view. // //============================================================================= using UnityEngine; using Valve.VR; namespace Valve.VR { [ExecuteInEditMode, RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))] public class SteamVR_Frustum : MonoBehaviour { public SteamVR_TrackedObject.EIndex index; public float fovLeft = 45, fovRight = 45, fovTop = 45, fovBottom = 45, nearZ = 0.5f, farZ = 2.5f; public void UpdateModel() { fovLeft = Mathf.Clamp(fovLeft, 1, 89); fovRight = Mathf.Clamp(fovRight, 1, 89); fovTop = Mathf.Clamp(fovTop, 1, 89); fovBottom = Mathf.Clamp(fovBottom, 1, 89); farZ = Mathf.Max(farZ, nearZ + 0.01f); nearZ = Mathf.Clamp(nearZ, 0.01f, farZ - 0.01f); var lsin = Mathf.Sin(-fovLeft * Mathf.Deg2Rad); var rsin = Mathf.Sin(fovRight * Mathf.Deg2Rad); var tsin = Mathf.Sin(fovTop * Mathf.Deg2Rad); var bsin = Mathf.Sin(-fovBottom * Mathf.Deg2Rad); var lcos = Mathf.Cos(-fovLeft * Mathf.Deg2Rad); var rcos = Mathf.Cos(fovRight * Mathf.Deg2Rad); var tcos = Mathf.Cos(fovTop * Mathf.Deg2Rad); var bcos = Mathf.Cos(-fovBottom * Mathf.Deg2Rad); var corners = new Vector3[] { new Vector3(lsin * nearZ / lcos, tsin * nearZ / tcos, nearZ), //tln new Vector3(rsin * nearZ / rcos, tsin * nearZ / tcos, nearZ), //trn new Vector3(rsin * nearZ / rcos, bsin * nearZ / bcos, nearZ), //brn new Vector3(lsin * nearZ / lcos, bsin * nearZ / bcos, nearZ), //bln new Vector3(lsin * farZ / lcos, tsin * farZ / tcos, farZ ), //tlf new Vector3(rsin * farZ / rcos, tsin * farZ / tcos, farZ ), //trf new Vector3(rsin * farZ / rcos, bsin * farZ / bcos, farZ ), //brf new Vector3(lsin * farZ / lcos, bsin * farZ / bcos, farZ ), //blf }; var triangles = new int[] { // 0, 1, 2, 0, 2, 3, // near // 0, 2, 1, 0, 3, 2, // near // 4, 5, 6, 4, 6, 7, // far // 4, 6, 5, 4, 7, 6, // far 0, 4, 7, 0, 7, 3, // left 0, 7, 4, 0, 3, 7, // left 1, 5, 6, 1, 6, 2, // right 1, 6, 5, 1, 2, 6, // right 0, 4, 5, 0, 5, 1, // top 0, 5, 4, 0, 1, 5, // top 2, 3, 7, 2, 7, 6, // bottom 2, 7, 3, 2, 6, 7, // bottom }; int j = 0; var vertices = new Vector3[triangles.Length]; var normals = new Vector3[triangles.Length]; for (int i = 0; i < triangles.Length / 3; i++) { var a = corners[triangles[i * 3 + 0]]; var b = corners[triangles[i * 3 + 1]]; var c = corners[triangles[i * 3 + 2]]; var n = Vector3.Cross(b - a, c - a).normalized; normals[i * 3 + 0] = n; normals[i * 3 + 1] = n; normals[i * 3 + 2] = n; vertices[i * 3 + 0] = a; vertices[i * 3 + 1] = b; vertices[i * 3 + 2] = c; triangles[i * 3 + 0] = j++; triangles[i * 3 + 1] = j++; triangles[i * 3 + 2] = j++; } var mesh = new Mesh(); mesh.vertices = vertices; mesh.normals = normals; mesh.triangles = triangles; GetComponent().mesh = mesh; } private void OnDeviceConnected(int i, bool connected) { if (i != (int)index) return; GetComponent().mesh = null; if (connected) { var system = OpenVR.System; if (system != null && system.GetTrackedDeviceClass((uint)i) == ETrackedDeviceClass.TrackingReference) { var error = ETrackedPropertyError.TrackedProp_Success; var result = system.GetFloatTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_FieldOfViewLeftDegrees_Float, ref error); if (error == ETrackedPropertyError.TrackedProp_Success) fovLeft = result; result = system.GetFloatTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_FieldOfViewRightDegrees_Float, ref error); if (error == ETrackedPropertyError.TrackedProp_Success) fovRight = result; result = system.GetFloatTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_FieldOfViewTopDegrees_Float, ref error); if (error == ETrackedPropertyError.TrackedProp_Success) fovTop = result; result = system.GetFloatTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_FieldOfViewBottomDegrees_Float, ref error); if (error == ETrackedPropertyError.TrackedProp_Success) fovBottom = result; result = system.GetFloatTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_TrackingRangeMinimumMeters_Float, ref error); if (error == ETrackedPropertyError.TrackedProp_Success) nearZ = result; result = system.GetFloatTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_TrackingRangeMaximumMeters_Float, ref error); if (error == ETrackedPropertyError.TrackedProp_Success) farZ = result; UpdateModel(); } } } void OnEnable() { GetComponent().mesh = null; SteamVR_Events.DeviceConnected.Listen(OnDeviceConnected); } void OnDisable() { SteamVR_Events.DeviceConnected.Remove(OnDeviceConnected); GetComponent().mesh = null; } #if UNITY_EDITOR void Update() { if (!Application.isPlaying) UpdateModel(); } #endif } }