Files
beyond/Assets/Scripts/PortalCamera.cs
2024-11-20 15:21:28 +01:00

221 lines
7.4 KiB
C#

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<Camera>();
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<Camera>();
//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<MeshFilter>();
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);
}
}
}