using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Beyond { [RequireComponent(typeof(Camera))] [ExecuteAlways] public class PortalCamera : MonoBehaviour { public GameObject target; Camera mCam; public Vector3 xVec = new Vector3(1, 0, 0); public Vector3 yVec = new Vector3(0, 1, 0); public Vector3 zVec = new Vector3(0, 0, -1); MeshFilter mMesh; Bounds bounds; Vector4[] quadPoints = new Vector4[4]; Vector4[] camSpaceQuadPoints = new Vector4[4]; // Start is called before the first frame update void Start() { mCam = GetComponent(); CalculateQuadPoints(); } public float CalculateFOV() { float correction = 1f; /* if (targetQuad) { mCam.transform.LookAt(targetQuad.transform); Vector3 forward = transform.InverseTransformDirection(targetQuad.transform.forward); correction = -forward.x / forward.z; } */ float zPlaneDist = 0f; zPlaneDist = Vector3.Dot(-target.transform.forward, transform.position); float fov = Mathf.Atan2(target.transform.localScale.x / correction, zPlaneDist) * Mathf.Rad2Deg * 2.0f; float fov2 = Mathf.Atan2(target.transform.localScale.x, -transform.localPosition.z) * Mathf.Rad2Deg; Debug.Log("FOV: " + fov); return fov; } static public Matrix4x4 CalculateOffcenterProjectionMatrix(float near, float far, float left, float right, float top, float bottom) { Matrix4x4 m = Matrix4x4.identity; float x = 2.0F * near / (right - left); float y = 2.0F * near / (top - bottom); float a = (right + left) / (right - left); float b = (top + bottom) / (top - bottom); float c = -(far + near) / (far - near); float d = -(2.0F * far * near) / (far - near); float e = -1.0F; m[0, 0] = x; m[0, 1] = 0; m[0, 2] = a; m[0, 3] = 0; m[1, 0] = 0; m[1, 1] = y; m[1, 2] = b; m[1, 3] = 0; m[2, 0] = 0; m[2, 1] = 0; m[2, 2] = c; m[2, 3] = d; m[3, 0] = 0; m[3, 1] = 0; m[3, 2] = e; m[3, 3] = 0; return m; } static public Matrix4x4 CalculateProjectionMatrix(float near, float far, float aspect, float fov) { Matrix4x4 m = Matrix4x4.identity; float left, right, top, bottom; float scale = Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad) * near; right = aspect * scale; left = -right; top = scale; bottom = -top; float x = 2.0F * near / (right - left); float y = 2.0F * near / (top - bottom); float a = (right + left) / (right - left); float b = (top + bottom) / (top - bottom); float c = -(far + near) / (far - near); float d = -(2.0F * far * near) / (far - near); float e = -1.0F; m[0, 0] = x; m[0, 1] = 0; m[0, 2] = a; m[0, 3] = 0; m[1, 0] = 0; m[1, 1] = y; m[1, 2] = b; m[1, 3] = 0; m[2, 0] = 0; m[2, 1] = 0; m[2, 2] = c; m[2, 3] = d; m[3, 0] = 0; m[3, 1] = 0; m[3, 2] = e; m[3, 3] = 0; return m; } // Update is called once per frame void Update() { } void CalculateQuadPoints() { Vector3 pos = target.transform.TransformPoint(bounds.center); Vector3 right = target.transform.right * bounds.extents.x; Vector3 up = target.transform.up * bounds.extents.y; quadPoints[0] = pos + right + up; quadPoints[1] = pos - right + up; quadPoints[2] = pos - right - up; quadPoints[3] = pos + right - up; for (int i = 0; i < 4; i++) quadPoints[i].w = 1; } void CalculateCamSpacePoints() { for (int i = 0; i < 4; i++) { camSpaceQuadPoints[i] = mCam.worldToCameraMatrix * quadPoints[i]; camSpaceQuadPoints[i] /= camSpaceQuadPoints[i].w; } } private void OnDrawGizmosSelected() { mCam = GetComponent(); //Gizmos.DrawCube() Vector4[] cubePoints = new Vector4[8]; Matrix4x4 mat = (mCam.projectionMatrix * mCam.worldToCameraMatrix).inverse; for (int i = 0; i < 8; i++) { cubePoints[i] = new Vector4(i % 2 == 0 ? -1f : 1f, (i / 2) % 2 == 0 ? -1f : 1f, (i / 4) % 2 == 0 ? -1f : 1f, 1f); cubePoints[i] = mat * cubePoints[i]; cubePoints[i] /= cubePoints[i].w; } Gizmos.color = Color.red; Gizmos.DrawLine(cubePoints[0], cubePoints[1]); Gizmos.DrawLine(cubePoints[2], cubePoints[3]); Gizmos.DrawLine(cubePoints[0], cubePoints[2]); Gizmos.DrawLine(cubePoints[1], cubePoints[3]); Gizmos.DrawLine(cubePoints[4], cubePoints[5]); Gizmos.DrawLine(cubePoints[6], cubePoints[7]); Gizmos.DrawLine(cubePoints[4], cubePoints[6]); Gizmos.DrawLine(cubePoints[5], cubePoints[7]); Gizmos.DrawLine(cubePoints[0], cubePoints[4]); Gizmos.DrawLine(cubePoints[1], cubePoints[5]); Gizmos.DrawLine(cubePoints[2], cubePoints[6]); Gizmos.DrawLine(cubePoints[3], cubePoints[7]); CalculateQuadPoints(); CalculateCamSpacePoints(); for (int i = 0; i < 4; i++) Gizmos.DrawLine(camSpaceQuadPoints[i], camSpaceQuadPoints[(i + 1) % 4]); // for (int i = 0; i < 4; i++) // Gizmos.DrawLine(camSpaceQuadPoints[i], camSpaceQuadPoints[(i + 1) % 4]); //Gizmos.DrawLine(cubePoints[3], cubePoints[0]); } // Update is called once per frame void LateUpdate() { if (!target) return; if (!mMesh) { mMesh = target.GetComponent(); bounds = mMesh.mesh.bounds; } transform.up = Vector3.Cross(transform.right, transform.forward); transform.forward = target.transform.forward; transform.right = target.transform.right; Vector3 forward = transform.InverseTransformDirection(target.transform.forward); xVec.z = -forward.x / forward.z; yVec.z = -forward.y / forward.z; Matrix4x4 skewMat = Matrix4x4.identity; skewMat.SetColumn(0, xVec); skewMat.SetColumn(1, yVec); skewMat.SetColumn(2, zVec); mCam.worldToCameraMatrix = skewMat * transform.worldToLocalMatrix; CalculateQuadPoints(); CalculateCamSpacePoints(); mCam.projectionMatrix = CalculateOffcenterProjectionMatrix(-camSpaceQuadPoints[0].z, 1000f, camSpaceQuadPoints[1].x, camSpaceQuadPoints[0].x, camSpaceQuadPoints[0].y, camSpaceQuadPoints[2].y); } } }