using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// Creates lines and meshes to display the frustum of the specified camera. /// Used to draw the frustum in the MR Calibration scene. /// [RequireComponent(typeof(LineRenderer))] public class FrustumDisplay : MonoBehaviour { /// /// ZEDManager instance whose OnZEDReady event we subscribe to to know when to draw the frustum. /// [Tooltip("ZEDManager instance whose OnZEDReady event we subscribe to to know when to draw the frustum. ")] public ZEDManager zedManager; /// /// Camera whose frustum to draw. Normally the left camera in a ZED_Rig_Mono or ZED_Rig_Stereo prefab. /// [Tooltip("Camera whose frustum to draw. Normally the left camera in a ZED_Rig_Mono or ZED_Rig_Stereo prefab. ")] public Camera cam; /// /// How far away to draw the camera's near plane. We don't use the real one as it's sometimes not visually helpful. /// [Tooltip("How far away to draw the camera's near plane. We don't use the real one as it's sometimes not visually helpful.")] public float nearPlaneRendDist = 0.1f; /// /// How far away to draw the camera's far plane. We don't use the real one as it'll usually make the frustum way too big to be helpful. /// [Tooltip("How far away to draw the camera's far plane. We don't use the real one as it'll usually make the frustum way too big to be helpful.")] public float farPlaneRendDist = 0.75f; /// /// Line renderer object used to draw the near plane. Will also be used as a basis for the edges connecting the near and far plane corners. /// [Tooltip("Line renderer object used to draw the near plane. Will also be used as a basis for the edges connecting the near and far plane corners.")] public LineRenderer nearPlaneLineRend; /// /// Material used to draw the lines on the sides of the frustum. /// [Tooltip("Material used to draw the lines on the sides of the frustum. ")] public Material edgeLineMat; /// /// Material used to draw the planes on the sides of the frustum. /// [Tooltip("Material used to draw the planes on the sides of the frustum. ")] public Material sidePlaneMat; /// /// Color (with alpha) of the edge lines on the sides of the frustum, nearest to the camera. /// [Tooltip("Color (with alpha) of the edge lines on the sides of the frustum, nearest to the camera. ")] [Space(5)] public Color edgeLineStartColor = Color.cyan; /// /// Color (with alpha) of the edge lines on the sides of the frustum, furthest to the camera. Used to create fade-out effect. /// [Tooltip("Color (with alpha) of the edge lines on the sides of the frustum, furthest to the camera. Used to create fade-out effect.")] public Color edgeLineEndColor = new Color(0, 1, 1, 0.2f); private LineRenderer topLeftLineRend; private LineRenderer topRightLineRend; private LineRenderer bottomLeftLineRend; private LineRenderer bottomRightLineRend; private MeshFilter topPlaneMF; private MeshFilter bottomPlaneMF; private MeshFilter leftPlaneMF; private MeshFilter rightPlaneMF; private List planeRenderers = new List(); // Use this for initialization void Awake () { if (!zedManager) ZEDManager.GetInstance(sl.ZED_CAMERA_ID.CAMERA_ID_01); if(!cam) cam = zedManager.GetLeftCamera(); zedManager.OnZEDReady += DrawFrustum; if(!nearPlaneLineRend) nearPlaneLineRend = GetComponent(); CreateEdgeLinesAndPlanes(); } /// /// Use the camera's frustum to draw a visualization of it, using line renderers and mesh objects mostly created /// earlier by CreateEdgeLinesAndPlanes(). /// private void DrawFrustum() { //Calculate the near plane. Vector3[] nearplane = new Vector3[4]; //cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), cam.nearClipPlane, Camera.MonoOrStereoscopicEye.Mono, nearplane); cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), nearPlaneRendDist, Camera.MonoOrStereoscopicEye.Mono, nearplane); nearPlaneLineRend.positionCount = 4; nearPlaneLineRend.SetPositions(nearplane); nearPlaneLineRend.loop = true; //Important to have Loop enabled or else one side will be open. if (edgeLineMat != null) nearPlaneLineRend.material = edgeLineMat; //Calculate the far plane that the ends extend to. Vector3[] farplane = new Vector3[4]; cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), farPlaneRendDist, Camera.MonoOrStereoscopicEye.Mono, farplane); bottomLeftLineRend.SetPositions(new Vector3[2] { nearplane[0], farplane[0] }); topLeftLineRend.SetPositions(new Vector3[2] { nearplane[1], farplane[1] }); topRightLineRend.SetPositions(new Vector3[2] { nearplane[2], farplane[2] }); bottomRightLineRend.SetPositions(new Vector3[2] { nearplane[3], farplane[3] }); //Set colors on edge lines. bottomLeftLineRend.startColor = edgeLineStartColor; topLeftLineRend.startColor = edgeLineStartColor; topRightLineRend.startColor = edgeLineStartColor; bottomRightLineRend.startColor = edgeLineStartColor; bottomLeftLineRend.endColor = edgeLineEndColor; topLeftLineRend.endColor = edgeLineEndColor; topRightLineRend.endColor = edgeLineEndColor; bottomRightLineRend.endColor = edgeLineEndColor; //Force the near plane box to have the same colors, but in this case without the fade that comes from the end color. nearPlaneLineRend.startColor = edgeLineStartColor; nearPlaneLineRend.endColor = edgeLineStartColor; //Not a typo. We don't want the fade. //Create the plane meshes on the sides and assign them. topPlaneMF.mesh = CreatePlaneMesh(nearplane[1], nearplane[2], farplane[1], farplane[2]); bottomPlaneMF.mesh = CreatePlaneMesh(nearplane[0], nearplane[3], farplane[0], farplane[3]); leftPlaneMF.mesh = CreatePlaneMesh(nearplane[0], nearplane[1], farplane[0], farplane[1]); rightPlaneMF.mesh = CreatePlaneMesh(nearplane[2], nearplane[3], farplane[2], farplane[3]); } /// /// Creates necessary objects for drawing the edge lines and planes, and applies basic settings. /// private void CreateEdgeLinesAndPlanes() { GameObject blgo = new GameObject("Bottom Left Edge"); GameObject tlgo = new GameObject("Top Left Edge"); GameObject trgo = new GameObject("Top Right Edge"); GameObject brgo = new GameObject("Bottom Right Edge"); blgo.transform.parent = transform; tlgo.transform.parent = transform; trgo.transform.parent = transform; brgo.transform.parent = transform; blgo.layer = gameObject.layer; tlgo.layer = gameObject.layer; trgo.layer = gameObject.layer; brgo.layer = gameObject.layer; blgo.transform.localPosition = Vector3.zero; tlgo.transform.localPosition = Vector3.zero; trgo.transform.localPosition = Vector3.zero; brgo.transform.localPosition = Vector3.zero; blgo.transform.localRotation = Quaternion.identity; tlgo.transform.localRotation = Quaternion.identity; trgo.transform.localRotation = Quaternion.identity; brgo.transform.localRotation = Quaternion.identity; bottomLeftLineRend = blgo.AddComponent(); topLeftLineRend = tlgo.AddComponent(); topRightLineRend = trgo.AddComponent(); bottomRightLineRend = brgo.AddComponent(); bottomLeftLineRend.widthMultiplier = nearPlaneLineRend.widthMultiplier; topLeftLineRend.widthMultiplier = nearPlaneLineRend.widthMultiplier; topRightLineRend.widthMultiplier = nearPlaneLineRend.widthMultiplier; bottomRightLineRend.widthMultiplier = nearPlaneLineRend.widthMultiplier; bottomLeftLineRend.useWorldSpace = false; topLeftLineRend.useWorldSpace = false; topRightLineRend.useWorldSpace = false; bottomRightLineRend.useWorldSpace = false; if(edgeLineMat != null) { bottomLeftLineRend.material = edgeLineMat; topLeftLineRend.material = edgeLineMat; topRightLineRend.material = edgeLineMat; bottomRightLineRend.material = edgeLineMat; } //Create MeshFilters and cache them. //We add them to the corner GameObjects such that it's clockwise one place of each corner, though it doesn't technically matter. topPlaneMF = tlgo.AddComponent(); bottomPlaneMF = brgo.AddComponent(); leftPlaneMF = blgo.AddComponent(); rightPlaneMF = trgo.AddComponent(); //Create MeshRenderers, but they'll all have the same mat so their position doesn't MATter (lol) either. planeRenderers.Clear(); //In case this got called twice somehow. planeRenderers.Add(tlgo.AddComponent()); planeRenderers.Add(brgo.AddComponent()); planeRenderers.Add(blgo.AddComponent()); planeRenderers.Add(trgo.AddComponent()); //Assign the plane materials. if (sidePlaneMat != null) { foreach (MeshRenderer rend in planeRenderers) { rend.material = sidePlaneMat; } } } /// /// Creates a single plane mesh with the specified corners. /// private Mesh CreatePlaneMesh(Vector3 botleft, Vector3 botright, Vector3 topleft, Vector3 topright) { Vector3[] verts = new Vector3[] { botleft, botright, topleft, topright }; int[] tris = new int[] { 0, 2, 1, 2, 3, 1 }; //Vector2[] uvs = new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 0), new Vector2(1, 1) }; float width = botright.x - botleft.x; float ydif = topleft.y - botleft.y; float zdif = topleft.z - botleft.z; //float height = topleft.z - botleft.z; float height = Mathf.Sqrt((ydif * ydif) + (zdif * zdif)); Vector2[] uvs = new Vector2[] { new Vector2(0, 0), new Vector2(width, 0), new Vector2(0, height), new Vector2(width, height) }; Mesh returnmesh = new Mesh(); returnmesh.vertices = verts; returnmesh.triangles = tris; returnmesh.uv = uvs; return returnmesh; } }