142 lines
5.7 KiB
C#
142 lines
5.7 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class WorkingPortal : MonoBehaviour {
|
|
[Header ("Main Settings")]
|
|
public WorkingPortal linkedWorkingPortal;
|
|
public MeshRenderer screen;
|
|
public int recursionLimit = 5;
|
|
public bool visibleFromCamera = true;
|
|
|
|
[Header ("Advanced Settings")]
|
|
public float nearClipOffset = 0.05f;
|
|
public float nearClipLimit = 0.2f;
|
|
|
|
public GameObject myGameObject;
|
|
|
|
// Private variables
|
|
public RenderTexture viewTexture;
|
|
public Camera WorkingPortalCam;
|
|
Camera playerCam;
|
|
Material firstRecursionMat;
|
|
MeshFilter screenMeshFilter;
|
|
|
|
void Awake () {
|
|
myGameObject = gameObject;
|
|
WorkingPortalCam.enabled = false;
|
|
screenMeshFilter = screen.GetComponent<MeshFilter> ();
|
|
screen.material.SetInt ("displayMask", 1);
|
|
}
|
|
|
|
// Manually render the camera attached to this WorkingPortal
|
|
// Called after PreWorkingPortalRender, and before PostWorkingPortalRender
|
|
public void Render (Camera newplayerCam) {
|
|
playerCam = newplayerCam;
|
|
// Skip rendering the view from this WorkingPortal if player is not looking at the linked WorkingPortal
|
|
if (!visibleFromCamera) {
|
|
return;
|
|
}
|
|
|
|
CreateViewTexture ();
|
|
|
|
var localToWorldMatrix = playerCam.transform.localToWorldMatrix;
|
|
var renderPositions = new Vector3[recursionLimit];
|
|
var renderRotations = new Quaternion[recursionLimit];
|
|
|
|
int startIndex = 0;
|
|
WorkingPortalCam.projectionMatrix = playerCam.projectionMatrix;
|
|
for (int i = 0; i < recursionLimit; i++) {
|
|
if (i > 0) {
|
|
break;
|
|
}
|
|
localToWorldMatrix = transform.localToWorldMatrix * linkedWorkingPortal.transform.worldToLocalMatrix * localToWorldMatrix;
|
|
int renderOrderIndex = recursionLimit - i - 1;
|
|
renderPositions[renderOrderIndex] = localToWorldMatrix.GetColumn (3);
|
|
renderRotations[renderOrderIndex] = localToWorldMatrix.rotation;
|
|
|
|
WorkingPortalCam.transform.SetPositionAndRotation (renderPositions[renderOrderIndex], renderRotations[renderOrderIndex]);
|
|
startIndex = renderOrderIndex;
|
|
}
|
|
|
|
// Hide screen so that camera can see through WorkingPortal
|
|
screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;
|
|
linkedWorkingPortal.screen.material.SetInt ("displayMask", 0);
|
|
|
|
for (int i = startIndex; i < recursionLimit; i++) {
|
|
WorkingPortalCam.transform.SetPositionAndRotation (renderPositions[i], renderRotations[i]);
|
|
Debug.Log(string.Format("rendering at: ", renderPositions[i].x, renderPositions[i].y, renderPositions[i].z));
|
|
SetNearClipPlane ();
|
|
WorkingPortalCam.Render ();
|
|
|
|
if (i == startIndex) {
|
|
linkedWorkingPortal.screen.material.SetInt ("displayMask", 1);
|
|
}
|
|
}
|
|
|
|
// Unhide objects hidden at start of render
|
|
screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
|
|
}
|
|
|
|
void CreateViewTexture () {
|
|
if (viewTexture == null || viewTexture.width != Screen.width || viewTexture.height != Screen.height) {
|
|
if (viewTexture != null) {
|
|
viewTexture.Release ();
|
|
}
|
|
viewTexture = new RenderTexture (Screen.width, Screen.height, 0);
|
|
// Render the view from the WorkingPortal camera to the view texture
|
|
WorkingPortalCam.targetTexture = viewTexture;
|
|
// Display the view texture on the screen of the linked WorkingPortal
|
|
linkedWorkingPortal.screen.material.SetTexture ("_MainTex", viewTexture);
|
|
}
|
|
}
|
|
|
|
|
|
// Use custom projection matrix to align WorkingPortal camera's near clip plane with the surface of the WorkingPortal
|
|
// Note that this affects precision of the depth buffer, which can cause issues with effects like screenspace AO
|
|
void SetNearClipPlane () {
|
|
// Learning resource:
|
|
// http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
|
|
Transform clipPlane = transform;
|
|
int dot = System.Math.Sign (Vector3.Dot (clipPlane.forward, transform.position - WorkingPortalCam.transform.position));
|
|
|
|
Vector3 camSpacePos = WorkingPortalCam.worldToCameraMatrix.MultiplyPoint (clipPlane.position);
|
|
Vector3 camSpaceNormal = WorkingPortalCam.worldToCameraMatrix.MultiplyVector (clipPlane.forward) * dot;
|
|
float camSpaceDst = -Vector3.Dot (camSpacePos, camSpaceNormal) + nearClipOffset;
|
|
|
|
// Don't use oblique clip plane if very close to WorkingPortal as it seems this can cause some visual artifacts
|
|
if (Mathf.Abs (camSpaceDst) > nearClipLimit) {
|
|
Vector4 clipPlaneCameraSpace = new Vector4 (camSpaceNormal.x, camSpaceNormal.y, camSpaceNormal.z, camSpaceDst);
|
|
|
|
// Update projection based on new clip plane
|
|
// Calculate matrix with player cam so that player camera settings (fov, etc) are used
|
|
WorkingPortalCam.projectionMatrix = playerCam.CalculateObliqueMatrix (clipPlaneCameraSpace);
|
|
} else {
|
|
WorkingPortalCam.projectionMatrix = playerCam.projectionMatrix;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Some helper/convenience stuff:
|
|
*/
|
|
|
|
int SideOfWorkingPortal (Vector3 pos) {
|
|
return System.Math.Sign (Vector3.Dot (pos - transform.position, transform.forward));
|
|
}
|
|
|
|
bool SameSideOfWorkingPortal (Vector3 posA, Vector3 posB) {
|
|
return SideOfWorkingPortal (posA) == SideOfWorkingPortal (posB);
|
|
}
|
|
|
|
Vector3 WorkingPortalCamPos {
|
|
get {
|
|
return WorkingPortalCam.transform.position;
|
|
}
|
|
}
|
|
|
|
void OnValidate () {
|
|
if (linkedWorkingPortal != null) {
|
|
linkedWorkingPortal.linkedWorkingPortal = this;
|
|
}
|
|
}
|
|
} |