added portals
This commit is contained in:
8
Assets/_PROJECT/Components/Portals3/Scripts/Core.meta
Normal file
8
Assets/_PROJECT/Components/Portals3/Scripts/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d32c080a4487499298dfca4d0bf1243
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,103 @@
|
||||
using UnityEngine;
|
||||
|
||||
public static class CameraUtility {
|
||||
static readonly Vector3[] cubeCornerOffsets = {
|
||||
new Vector3 (1, 1, 1),
|
||||
new Vector3 (-1, 1, 1),
|
||||
new Vector3 (-1, -1, 1),
|
||||
new Vector3 (-1, -1, -1),
|
||||
new Vector3 (-1, 1, -1),
|
||||
new Vector3 (1, -1, -1),
|
||||
new Vector3 (1, 1, -1),
|
||||
new Vector3 (1, -1, 1),
|
||||
};
|
||||
|
||||
// http://wiki.unity3d.com/index.php/IsVisibleFrom
|
||||
public static bool VisibleFromCamera (Renderer renderer, Camera camera) {
|
||||
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes (camera);
|
||||
return GeometryUtility.TestPlanesAABB (frustumPlanes, renderer.bounds);
|
||||
}
|
||||
|
||||
public static bool BoundsOverlap (MeshFilter nearObject, MeshFilter farObject, Camera camera) {
|
||||
|
||||
var near = GetScreenRectFromBounds (nearObject, camera);
|
||||
var far = GetScreenRectFromBounds (farObject, camera);
|
||||
|
||||
// ensure far object is indeed further away than near object
|
||||
if (far.zMax > near.zMin) {
|
||||
// Doesn't overlap on x axis
|
||||
if (far.xMax < near.xMin || far.xMin > near.xMax) {
|
||||
return false;
|
||||
}
|
||||
// Doesn't overlap on y axis
|
||||
if (far.yMax < near.yMin || far.yMin > near.yMax) {
|
||||
return false;
|
||||
}
|
||||
// Overlaps
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// With thanks to http://www.turiyaware.com/a-solution-to-unitys-camera-worldtoscreenpoint-causing-ui-elements-to-display-when-object-is-behind-the-camera/
|
||||
public static MinMax3D GetScreenRectFromBounds (MeshFilter renderer, Camera mainCamera) {
|
||||
MinMax3D minMax = new MinMax3D (float.MaxValue, float.MinValue);
|
||||
|
||||
Vector3[] screenBoundsExtents = new Vector3[8];
|
||||
var localBounds = renderer.sharedMesh.bounds;
|
||||
bool anyPointIsInFrontOfCamera = false;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Vector3 localSpaceCorner = localBounds.center + Vector3.Scale (localBounds.extents, cubeCornerOffsets[i]);
|
||||
Vector3 worldSpaceCorner = renderer.transform.TransformPoint (localSpaceCorner);
|
||||
Vector3 viewportSpaceCorner = mainCamera.WorldToViewportPoint (worldSpaceCorner);
|
||||
|
||||
if (viewportSpaceCorner.z > 0) {
|
||||
anyPointIsInFrontOfCamera = true;
|
||||
} else {
|
||||
// If point is behind camera, it gets flipped to the opposite side
|
||||
// So clamp to opposite edge to correct for this
|
||||
viewportSpaceCorner.x = (viewportSpaceCorner.x <= 0.5f) ? 1 : 0;
|
||||
viewportSpaceCorner.y = (viewportSpaceCorner.y <= 0.5f) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Update bounds with new corner point
|
||||
minMax.AddPoint (viewportSpaceCorner);
|
||||
}
|
||||
|
||||
// All points are behind camera so just return empty bounds
|
||||
if (!anyPointIsInFrontOfCamera) {
|
||||
return new MinMax3D ();
|
||||
}
|
||||
|
||||
return minMax;
|
||||
}
|
||||
|
||||
public struct MinMax3D {
|
||||
public float xMin;
|
||||
public float xMax;
|
||||
public float yMin;
|
||||
public float yMax;
|
||||
public float zMin;
|
||||
public float zMax;
|
||||
|
||||
public MinMax3D (float min, float max) {
|
||||
this.xMin = min;
|
||||
this.xMax = max;
|
||||
this.yMin = min;
|
||||
this.yMax = max;
|
||||
this.zMin = min;
|
||||
this.zMax = max;
|
||||
}
|
||||
|
||||
public void AddPoint (Vector3 point) {
|
||||
xMin = Mathf.Min (xMin, point.x);
|
||||
xMax = Mathf.Max (xMax, point.x);
|
||||
yMin = Mathf.Min (yMin, point.y);
|
||||
yMax = Mathf.Max (yMax, point.y);
|
||||
zMin = Mathf.Min (zMin, point.z);
|
||||
zMax = Mathf.Max (zMax, point.z);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e43c024bb46ea4cce9a2be741f27141d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class MainCamera : MonoBehaviour {
|
||||
|
||||
Portal[] portals;
|
||||
|
||||
void Awake () {
|
||||
portals = FindObjectsOfType<Portal> ();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
//Debug.Log("The number of found portals is: " + portals.Length);
|
||||
for (int i = 0; i < portals.Length; i++) {
|
||||
portals[i].PrePortalRender ();
|
||||
}
|
||||
for (int i = 0; i < portals.Length; i++) {
|
||||
portals[i].Render ();
|
||||
}
|
||||
|
||||
for (int i = 0; i < portals.Length; i++) {
|
||||
portals[i].PostPortalRender ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a42331d6ec5746d4960a877e58cfb58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
337
Assets/_PROJECT/Components/Portals3/Scripts/Core/Portal.cs
Normal file
337
Assets/_PROJECT/Components/Portals3/Scripts/Core/Portal.cs
Normal file
@@ -0,0 +1,337 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Design;
|
||||
using UnityEngine;
|
||||
|
||||
public class Portal : MonoBehaviour {
|
||||
[Header ("Main Settings")]
|
||||
public Portal linkedPortal;
|
||||
public MeshRenderer screen;
|
||||
public int recursionLimit = 1;
|
||||
|
||||
[Header ("Advanced Settings")]
|
||||
public float nearClipOffset = 0.05f;
|
||||
public float nearClipLimit = 0.2f;
|
||||
|
||||
// Private variables
|
||||
RenderTexture viewTexture;
|
||||
Camera portalCam;
|
||||
Camera playerCam;
|
||||
Material firstRecursionMat;
|
||||
List<PortalTraveller> trackedTravellers;
|
||||
MeshFilter screenMeshFilter;
|
||||
|
||||
void Awake () {
|
||||
portalCam = GetComponentInChildren<Camera> ();
|
||||
portalCam.enabled = false;
|
||||
trackedTravellers = new List<PortalTraveller> ();
|
||||
screenMeshFilter = screen.GetComponent<MeshFilter> ();
|
||||
screen.material.SetInt ("displayMask", 1);
|
||||
}
|
||||
|
||||
void LateUpdate () {
|
||||
if (playerCam != null)
|
||||
HandleTravellers ();
|
||||
|
||||
}
|
||||
|
||||
void HandleTravellers () {
|
||||
|
||||
for (int i = 0; i < trackedTravellers.Count; i++) {
|
||||
PortalTraveller traveller = trackedTravellers[i];
|
||||
Transform travellerT = traveller.transform;
|
||||
var m = linkedPortal.transform.localToWorldMatrix * transform.worldToLocalMatrix * travellerT.localToWorldMatrix;
|
||||
|
||||
Vector3 offsetFromPortal = travellerT.position - transform.position;
|
||||
int portalSide = System.Math.Sign (Vector3.Dot (offsetFromPortal, transform.forward));
|
||||
int portalSideOld = System.Math.Sign (Vector3.Dot (traveller.previousOffsetFromPortal, transform.forward));
|
||||
// Teleport the traveller if it has crossed from one side of the portal to the other
|
||||
if (portalSide != portalSideOld) {
|
||||
var positionOld = travellerT.position;
|
||||
var rotOld = travellerT.rotation;
|
||||
traveller.Teleport (transform, linkedPortal.transform, m.GetColumn (3), m.rotation);
|
||||
traveller.graphicsClone.transform.SetPositionAndRotation (positionOld, rotOld);
|
||||
// Can't rely on OnTriggerEnter/Exit to be called next frame since it depends on when FixedUpdate runs
|
||||
linkedPortal.OnTravellerEnterPortal (traveller);
|
||||
trackedTravellers.RemoveAt (i);
|
||||
i--;
|
||||
|
||||
} else {
|
||||
traveller.graphicsClone.transform.SetPositionAndRotation (m.GetColumn (3), m.rotation);
|
||||
//UpdateSliceParams (traveller);
|
||||
traveller.previousOffsetFromPortal = offsetFromPortal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called before any portal cameras are rendered for the current frame
|
||||
public void PrePortalRender () {
|
||||
if (playerCam == null) return;
|
||||
foreach (var traveller in trackedTravellers) {
|
||||
UpdateSliceParams (traveller);
|
||||
}
|
||||
}
|
||||
|
||||
// Manually render the camera attached to this portal
|
||||
// Called after PrePortalRender, and before PostPortalRender
|
||||
public void Render () {
|
||||
if (playerCam == null) return;
|
||||
//Debug.Log("Rendering portal");
|
||||
// Skip rendering the view from this portal if player is not looking at the linked portal
|
||||
/*if (!CameraUtility.VisibleFromCamera (linkedPortal.screen, playerCam)) {
|
||||
return;
|
||||
}*/
|
||||
|
||||
CreateViewTexture ();
|
||||
|
||||
var localToWorldMatrix = playerCam.transform.localToWorldMatrix;
|
||||
var renderPositions = new Vector3[recursionLimit];
|
||||
var renderRotations = new Quaternion[recursionLimit];
|
||||
|
||||
int startIndex = 0;
|
||||
portalCam.projectionMatrix = playerCam.projectionMatrix;
|
||||
for (int i = 0; i < recursionLimit; i++) {
|
||||
if (i > 0) {
|
||||
// No need for recursive rendering if linked portal is not visible through this portal
|
||||
if (!CameraUtility.BoundsOverlap (screenMeshFilter, linkedPortal.screenMeshFilter, portalCam)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
localToWorldMatrix = transform.localToWorldMatrix * linkedPortal.transform.worldToLocalMatrix * localToWorldMatrix;
|
||||
int renderOrderIndex = recursionLimit - i - 1;
|
||||
renderPositions[renderOrderIndex] = localToWorldMatrix.GetColumn (3);
|
||||
renderRotations[renderOrderIndex] = localToWorldMatrix.rotation;
|
||||
|
||||
//Debug.Log("Moving portalcam");
|
||||
portalCam.transform.SetPositionAndRotation (renderPositions[renderOrderIndex], renderRotations[renderOrderIndex]);
|
||||
startIndex = renderOrderIndex;
|
||||
}
|
||||
|
||||
// Hide screen so that camera can see through portal
|
||||
screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;
|
||||
linkedPortal.screen.material.SetInt ("displayMask", 0);;
|
||||
for (int i = startIndex; i < recursionLimit; i++) {
|
||||
portalCam.transform.SetPositionAndRotation (renderPositions[i], renderRotations[i]);
|
||||
SetNearClipPlane ();
|
||||
HandleClipping ();
|
||||
portalCam.Render ();
|
||||
|
||||
if (i == startIndex) {
|
||||
linkedPortal.screen.material.SetInt ("displayMask", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Unhide objects hidden at start of render
|
||||
screen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
|
||||
}
|
||||
|
||||
void HandleClipping () {
|
||||
// There are two main graphical issues when slicing travellers
|
||||
// 1. Tiny sliver of mesh drawn on backside of portal
|
||||
// Ideally the oblique clip plane would sort this out, but even with 0 offset, tiny sliver still visible
|
||||
// 2. Tiny seam between the sliced mesh, and the rest of the model drawn onto the portal screen
|
||||
// This function tries to address these issues by modifying the slice parameters when rendering the view from the portal
|
||||
// Would be great if this could be fixed more elegantly, but this is the best I can figure out for now
|
||||
const float hideDst = -1000;
|
||||
const float showDst = 1000;
|
||||
float screenThickness = linkedPortal.ProtectScreenFromClipping (portalCam.transform.position);
|
||||
|
||||
foreach (var traveller in trackedTravellers) {
|
||||
if (SameSideOfPortal (traveller.transform.position, portalCamPos)) {
|
||||
// Addresses issue 1
|
||||
traveller.SetSliceOffsetDst (hideDst, false);
|
||||
} else {
|
||||
// Addresses issue 2
|
||||
traveller.SetSliceOffsetDst (showDst, false);
|
||||
}
|
||||
|
||||
// Ensure clone is properly sliced, in case it's visible through this portal:
|
||||
int cloneSideOfLinkedPortal = -SideOfPortal (traveller.transform.position);
|
||||
bool camSameSideAsClone = linkedPortal.SideOfPortal (portalCamPos) == cloneSideOfLinkedPortal;
|
||||
if (camSameSideAsClone) {
|
||||
traveller.SetSliceOffsetDst (screenThickness, true);
|
||||
} else {
|
||||
traveller.SetSliceOffsetDst (-screenThickness, true);
|
||||
}
|
||||
}
|
||||
|
||||
var offsetFromPortalToCam = portalCamPos - transform.position;
|
||||
foreach (var linkedTraveller in linkedPortal.trackedTravellers) {
|
||||
var travellerPos = linkedTraveller.graphicsObject.transform.position;
|
||||
var clonePos = linkedTraveller.graphicsClone.transform.position;
|
||||
// Handle clone of linked portal coming through this portal:
|
||||
bool cloneOnSameSideAsCam = linkedPortal.SideOfPortal (travellerPos) != SideOfPortal (portalCamPos);
|
||||
if (cloneOnSameSideAsCam) {
|
||||
// Addresses issue 1
|
||||
linkedTraveller.SetSliceOffsetDst (hideDst, true);
|
||||
} else {
|
||||
// Addresses issue 2
|
||||
linkedTraveller.SetSliceOffsetDst (showDst, true);
|
||||
}
|
||||
|
||||
// Ensure traveller of linked portal is properly sliced, in case it's visible through this portal:
|
||||
bool camSameSideAsTraveller = linkedPortal.SameSideOfPortal (linkedTraveller.transform.position, portalCamPos);
|
||||
if (camSameSideAsTraveller) {
|
||||
linkedTraveller.SetSliceOffsetDst (screenThickness, false);
|
||||
} else {
|
||||
linkedTraveller.SetSliceOffsetDst (-screenThickness, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called once all portals have been rendered, but before the player camera renders
|
||||
public void PostPortalRender () {
|
||||
if (playerCam == null) return;
|
||||
foreach (var traveller in trackedTravellers) {
|
||||
UpdateSliceParams (traveller);
|
||||
}
|
||||
ProtectScreenFromClipping (playerCam.transform.position);
|
||||
}
|
||||
void CreateViewTexture () {
|
||||
//Debug.Log("creating view texture");
|
||||
if (viewTexture == null || viewTexture.width != Screen.width || viewTexture.height != Screen.height) {
|
||||
if (viewTexture != null) {
|
||||
viewTexture.Release ();
|
||||
}
|
||||
Debug.Log("creating view texture");
|
||||
viewTexture = new RenderTexture (Screen.width, Screen.height, 0);
|
||||
// Render the view from the portal camera to the view texture
|
||||
portalCam.targetTexture = viewTexture;
|
||||
// Display the view texture on the screen of the linked portal
|
||||
linkedPortal.screen.material.SetTexture ("_MainTex", viewTexture);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the thickness of the portal screen so as not to clip with camera near plane when player goes through
|
||||
float ProtectScreenFromClipping (Vector3 viewPoint) {
|
||||
|
||||
float halfHeight = playerCam.nearClipPlane * Mathf.Tan (playerCam.fieldOfView * 0.5f * Mathf.Deg2Rad);
|
||||
float halfWidth = halfHeight * playerCam.aspect;
|
||||
float dstToNearClipPlaneCorner = new Vector3 (halfWidth, halfHeight, playerCam.nearClipPlane).magnitude;
|
||||
float screenThickness = dstToNearClipPlaneCorner;
|
||||
|
||||
Transform screenT = screen.transform;
|
||||
bool camFacingSameDirAsPortal = Vector3.Dot (transform.forward, transform.position - viewPoint) > 0;
|
||||
screenT.localScale = new Vector3 (screenT.localScale.x, screenT.localScale.y, screenThickness);
|
||||
screenT.localPosition = Vector3.forward * screenThickness * ((camFacingSameDirAsPortal) ? 0.5f : -0.5f);
|
||||
return screenThickness;
|
||||
}
|
||||
|
||||
void UpdateSliceParams (PortalTraveller traveller) {
|
||||
// Calculate slice normal
|
||||
int side = SideOfPortal (traveller.transform.position);
|
||||
Vector3 sliceNormal = transform.forward * -side;
|
||||
Vector3 cloneSliceNormal = linkedPortal.transform.forward * side;
|
||||
|
||||
// Calculate slice centre
|
||||
Vector3 slicePos = transform.position;
|
||||
Vector3 cloneSlicePos = linkedPortal.transform.position;
|
||||
|
||||
// Adjust slice offset so that when player standing on other side of portal to the object, the slice doesn't clip through
|
||||
float sliceOffsetDst = 0;
|
||||
float cloneSliceOffsetDst = 0;
|
||||
float screenThickness = screen.transform.localScale.z;
|
||||
|
||||
bool playerSameSideAsTraveller = SameSideOfPortal (playerCam.transform.position, traveller.transform.position);
|
||||
if (!playerSameSideAsTraveller) {
|
||||
sliceOffsetDst = -screenThickness;
|
||||
}
|
||||
bool playerSameSideAsCloneAppearing = side != linkedPortal.SideOfPortal (playerCam.transform.position);
|
||||
if (!playerSameSideAsCloneAppearing) {
|
||||
cloneSliceOffsetDst = -screenThickness;
|
||||
}
|
||||
|
||||
// Apply parameters
|
||||
for (int i = 0; i < traveller.originalMaterials.Length; i++) {
|
||||
traveller.originalMaterials[i].SetVector ("sliceCentre", slicePos);
|
||||
traveller.originalMaterials[i].SetVector ("sliceNormal", sliceNormal);
|
||||
traveller.originalMaterials[i].SetFloat ("sliceOffsetDst", sliceOffsetDst);
|
||||
|
||||
traveller.cloneMaterials[i].SetVector ("sliceCentre", cloneSlicePos);
|
||||
traveller.cloneMaterials[i].SetVector ("sliceNormal", cloneSliceNormal);
|
||||
traveller.cloneMaterials[i].SetFloat ("sliceOffsetDst", cloneSliceOffsetDst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Use custom projection matrix to align portal camera's near clip plane with the surface of the portal
|
||||
// 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 - portalCam.transform.position));
|
||||
|
||||
Vector3 camSpacePos = portalCam.worldToCameraMatrix.MultiplyPoint (clipPlane.position);
|
||||
Vector3 camSpaceNormal = portalCam.worldToCameraMatrix.MultiplyVector (clipPlane.forward) * dot;
|
||||
float camSpaceDst = -Vector3.Dot (camSpacePos, camSpaceNormal) + nearClipOffset;
|
||||
|
||||
// Don't use oblique clip plane if very close to portal 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
|
||||
portalCam.projectionMatrix = playerCam.CalculateObliqueMatrix (clipPlaneCameraSpace);
|
||||
} else {
|
||||
portalCam.projectionMatrix = playerCam.projectionMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
void OnTravellerEnterPortal (PortalTraveller traveller) {
|
||||
if (!trackedTravellers.Contains (traveller)) {
|
||||
traveller.EnterPortalThreshold ();
|
||||
traveller.previousOffsetFromPortal = traveller.transform.position - transform.position;
|
||||
trackedTravellers.Add (traveller);
|
||||
}
|
||||
}
|
||||
public void AddPlayerCam(Camera cam)
|
||||
{
|
||||
portalCam.enabled = true;
|
||||
playerCam = cam;
|
||||
}
|
||||
public void RemovePlayerCam(Camera cam)
|
||||
{
|
||||
playerCam = null;
|
||||
portalCam.enabled = false;
|
||||
}
|
||||
void OnTriggerEnter (Collider other) {
|
||||
var traveller = other.GetComponent<PortalTraveller> ();
|
||||
if (traveller) {
|
||||
OnTravellerEnterPortal (traveller);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTriggerExit (Collider other) {
|
||||
var traveller = other.GetComponent<PortalTraveller> ();
|
||||
if (traveller && trackedTravellers.Contains (traveller)) {
|
||||
traveller.ExitPortalThreshold ();
|
||||
trackedTravellers.Remove (traveller);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Some helper/convenience stuff:
|
||||
*/
|
||||
|
||||
int SideOfPortal (Vector3 pos) {
|
||||
return System.Math.Sign (Vector3.Dot (pos - transform.position, transform.forward));
|
||||
}
|
||||
|
||||
bool SameSideOfPortal (Vector3 posA, Vector3 posB) {
|
||||
return SideOfPortal (posA) == SideOfPortal (posB);
|
||||
}
|
||||
|
||||
Vector3 portalCamPos {
|
||||
get {
|
||||
return portalCam.transform.position;
|
||||
}
|
||||
}
|
||||
|
||||
void OnValidate () {
|
||||
if (linkedPortal != null) {
|
||||
linkedPortal.linkedPortal = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57b9885b89b6d4fbe92f77801ecc3671
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class PortalTraveller : MonoBehaviour {
|
||||
|
||||
public GameObject graphicsObject;
|
||||
public GameObject graphicsClone { get; set; }
|
||||
public Vector3 previousOffsetFromPortal { get; set; }
|
||||
|
||||
public Material[] originalMaterials { get; set; }
|
||||
public Material[] cloneMaterials { get; set; }
|
||||
|
||||
public virtual void Teleport (Transform fromPortal, Transform toPortal, Vector3 pos, Quaternion rot) {
|
||||
transform.position = pos;
|
||||
transform.rotation = rot;
|
||||
}
|
||||
|
||||
// Called when first touches portal
|
||||
public virtual void EnterPortalThreshold () {
|
||||
if (graphicsClone == null) {
|
||||
graphicsClone = Instantiate (graphicsObject);
|
||||
graphicsClone.transform.parent = graphicsObject.transform.parent;
|
||||
graphicsClone.transform.localScale = graphicsObject.transform.localScale;
|
||||
originalMaterials = GetMaterials (graphicsObject);
|
||||
cloneMaterials = GetMaterials (graphicsClone);
|
||||
} else {
|
||||
graphicsClone.SetActive (true);
|
||||
}
|
||||
}
|
||||
|
||||
// Called once no longer touching portal (excluding when teleporting)
|
||||
public virtual void ExitPortalThreshold () {
|
||||
graphicsClone.SetActive (false);
|
||||
// Disable slicing
|
||||
for (int i = 0; i < originalMaterials.Length; i++) {
|
||||
originalMaterials[i].SetVector ("sliceNormal", Vector3.zero);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSliceOffsetDst (float dst, bool clone) {
|
||||
for (int i = 0; i < originalMaterials.Length; i++) {
|
||||
if (clone) {
|
||||
cloneMaterials[i].SetFloat ("sliceOffsetDst", dst);
|
||||
} else {
|
||||
originalMaterials[i].SetFloat ("sliceOffsetDst", dst);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Material[] GetMaterials (GameObject g) {
|
||||
var renderers = g.GetComponentsInChildren<MeshRenderer> ();
|
||||
var matList = new List<Material> ();
|
||||
foreach (var renderer in renderers) {
|
||||
foreach (var mat in renderer.materials) {
|
||||
matList.Add (mat);
|
||||
}
|
||||
}
|
||||
return matList.ToArray ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 906cf3fd207e5450784f73a2d4b57b4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a07a87d41098443a89d0eab9e9cc8b80
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
Shader "Custom/Portal"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_InactiveColour ("Inactive Colour", Color) = (1, 1, 1, 1)
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" }
|
||||
LOD 100
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float4 screenPos : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _InactiveColour;
|
||||
int displayMask; // set to 1 to display texture, otherwise will draw test colour
|
||||
|
||||
|
||||
v2f vert (appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.screenPos = ComputeScreenPos(o.vertex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
float2 uv = i.screenPos.xy / i.screenPos.w;
|
||||
fixed4 portalCol = tex2D(_MainTex, uv);
|
||||
return portalCol * displayMask + _InactiveColour * (1-displayMask);
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
Fallback "Standard" // for shadows
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 670c541ecf09d494896624500a1a3272
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
Shader "Custom/Slice"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1,1,1,1)
|
||||
_MainTex ("Albedo (RGB)", 2D) = "white" {}
|
||||
_Glossiness ("Smoothness", Range(0,1)) = 0.5
|
||||
_Metallic ("Metallic", Range(0,1)) = 0.0
|
||||
|
||||
sliceNormal("normal", Vector) = (0,0,0,0)
|
||||
sliceCentre ("centre", Vector) = (0,0,0,0)
|
||||
sliceOffsetDst("offset", Float) = 0
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags { "Queue" = "Geometry" "IgnoreProjector" = "True" "RenderType"="Geometry" }
|
||||
LOD 200
|
||||
|
||||
CGPROGRAM
|
||||
// Physically based Standard lighting model, and enable shadows on all light types
|
||||
#pragma surface surf Standard addshadow
|
||||
// Use shader model 3.0 target, to get nicer looking lighting
|
||||
#pragma target 3.0
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
struct Input
|
||||
{
|
||||
float2 uv_MainTex;
|
||||
float3 worldPos;
|
||||
};
|
||||
|
||||
half _Glossiness;
|
||||
half _Metallic;
|
||||
fixed4 _Color;
|
||||
|
||||
// World space normal of slice, anything along this direction from centre will be invisible
|
||||
float3 sliceNormal;
|
||||
// World space centre of slice
|
||||
float3 sliceCentre;
|
||||
// Increasing makes more of the mesh visible, decreasing makes less of the mesh visible
|
||||
float sliceOffsetDst;
|
||||
|
||||
void surf (Input IN, inout SurfaceOutputStandard o)
|
||||
{
|
||||
float3 adjustedCentre = sliceCentre + sliceNormal * sliceOffsetDst;
|
||||
float3 offsetToSliceCentre = adjustedCentre - IN.worldPos;
|
||||
clip (dot(offsetToSliceCentre, sliceNormal));
|
||||
|
||||
// Albedo comes from a texture tinted by color
|
||||
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
|
||||
o.Albedo = c.rgb;
|
||||
|
||||
// Metallic and smoothness come from slider variables
|
||||
o.Metallic = _Metallic;
|
||||
o.Smoothness = _Glossiness;
|
||||
o.Alpha = c.a;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
FallBack "VertexLit"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aaeaf3dc8c9344c5fa572f7789c131cc
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class PortalProximitySensor : MonoBehaviour
|
||||
{
|
||||
public Portal Portal1;
|
||||
public Portal Portal2;
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
//Debug.Log("Collider entered portal proximity");
|
||||
if (other.GetComponent<PortalTraveller>() == null || other.GetComponentInChildren<Camera>() == null) return;
|
||||
Portal1.AddPlayerCam(other.GetComponentInChildren<Camera>());
|
||||
Portal2.AddPlayerCam(other.GetComponentInChildren<Camera>());
|
||||
//Debug.Log("Portal is updated");
|
||||
}
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (other.GetComponent<PortalTraveller>() == null || other.GetComponentInChildren<Camera>() == null) return;
|
||||
Portal1.RemovePlayerCam(other.GetComponentInChildren<Camera>());
|
||||
Portal2.RemovePlayerCam(other.GetComponentInChildren<Camera>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75b7c559b5a851b43adc03f31a03766e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user