Skip to content

Instantly share code, notes, and snippets.

@lukasreuter
Created January 28, 2019 21:30
Show Gist options
  • Save lukasreuter/083ad923c1ed17080188a8edac7e1722 to your computer and use it in GitHub Desktop.
Save lukasreuter/083ad923c1ed17080188a8edac7e1722 to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using Assets.Scripts;
using UnityEngine;
using Valve.VR;
namespace Assets {
public class Portal : MonoBehaviour {
private static readonly Dictionary<StereoTargetEyeMask, Rect> Viewports = new Dictionary<StereoTargetEyeMask, Rect> {
{ StereoTargetEyeMask.None, new Rect(0, 0, 1, 1) },
{ StereoTargetEyeMask.Left, new Rect(0, 0, .5f, 1) },
{ StereoTargetEyeMask.Right, new Rect(.5f, 0, .5f, 1) },
{ StereoTargetEyeMask.Both, new Rect(0, 0, 1, 1) }
};
private static Lock Lock = new Lock(false);
public Transform Destination;
public Camera PlayerCamera;
private new Camera camera;
public void Start() {
var texture = new RenderTexture(2048, 2048, 24);
if (PlayerCamera.stereoEnabled) texture.width *= 2;
texture.antiAliasing = Mathf.Max(1, QualitySettings.antiAliasing);
var cameraObject = new GameObject(name + " Camera");
cameraObject.transform.parent = transform.parent;
camera = cameraObject.SummonComponent<Camera>();
camera.enabled = false;
camera.clearFlags = PlayerCamera.clearFlags;
camera.backgroundColor = PlayerCamera.backgroundColor;
camera.farClipPlane = PlayerCamera.farClipPlane;
camera.nearClipPlane = PlayerCamera.nearClipPlane;
camera.orthographic = PlayerCamera.orthographic;
camera.fieldOfView = PlayerCamera.fieldOfView;
camera.aspect = PlayerCamera.aspect;
camera.orthographicSize = PlayerCamera.orthographicSize;
camera.targetTexture = texture;
camera.cullingMask &= ~(1 << LayerMask.NameToLayer("Portal"));
if (camera.clearFlags == CameraClearFlags.Skybox) {
var skybox = cameraObject.SummonComponent<Skybox>();
skybox.enabled = true;
skybox.material = PlayerCamera.SummonComponent<Skybox>().material;
}
var propertyBlock = new MaterialPropertyBlock();
propertyBlock.SetTexture("_PortalTexture", texture);
var renderers = GetComponentsInChildren<Renderer>();
foreach (var renderer in renderers) {
renderer.sharedMaterial = new Material(Shader.Find("Custom/Portal"));
renderer.SetPropertyBlock(propertyBlock);
}
}
public void OnCollisionStay(Collision collision) {
if (collision.gameObject.GetComponent<Camera>() == PlayerCamera) {
transform.FindChild("Back").gameObject.SetActive(true);
transform.FindChild("Top").gameObject.SetActive(true);
transform.FindChild("Bottom").gameObject.SetActive(true);
transform.FindChild("Left").gameObject.SetActive(true);
transform.FindChild("Right").gameObject.SetActive(true);
}
}
public void OnCollisionExit(Collision collision) {
if (collision.gameObject.GetComponent<Camera>() == PlayerCamera) {
transform.FindChild("Back").gameObject.SetActive(false);
transform.FindChild("Top").gameObject.SetActive(false);
transform.FindChild("Bottom").gameObject.SetActive(false);
transform.FindChild("Left").gameObject.SetActive(false);
transform.FindChild("Right").gameObject.SetActive(false);
PlayerCamera.transform.parent.position += Destination.position - transform.position;
}
}
public void OnWillRenderObject() {
if (Lock.Engaged || Camera.current != PlayerCamera || !camera.isActiveAndEnabled) {
return;
}
using (Lock = new Lock()) {
if (!PlayerCamera.stereoEnabled || camera.stereoTargetEye == StereoTargetEyeMask.None) {
RenderCamera(PlayerCamera.transform.position, PlayerCamera.transform.rotation, PlayerCamera.projectionMatrix, Viewports[camera.stereoTargetEye]);
return;
}
switch (camera.stereoTargetEye) {
case StereoTargetEyeMask.Both:
RenderEye(StereoTargetEyeMask.Left);
RenderEye(StereoTargetEyeMask.Right);
break;
default:
RenderEye(camera.stereoTargetEye);
break;
}
}
}
private void RenderEye(StereoTargetEyeMask eye) {
var eyeIndex = (int)eye - 1;
var eyePos = PlayerCamera.transform.TransformPoint(SteamVR.instance.eyes[eyeIndex].pos);
var eyeRot = PlayerCamera.transform.rotation * SteamVR.instance.eyes[eyeIndex].rot;
var projectionMatrix = GetSteamVrProjectionMatrix(PlayerCamera, (EVREye)eyeIndex);
RenderCamera(eyePos, eyeRot, projectionMatrix, Viewports[eye]);
}
private void RenderCamera(Vector3 playerPosition, Quaternion playerRotation, Matrix4x4 playerProjectionMatrix, Rect playerViewport) {
camera.projectionMatrix = playerProjectionMatrix;
camera.rect = playerViewport;
var angleSign = Vector3.Cross(Vector3.Scale(transform.forward, Vector3Helper.XZ), Vector3.Scale(Destination.forward, Vector3Helper.XZ)).normalized.y;
var relativeAngle = angleSign * Vector2.Angle(new Vector2(transform.forward.x, transform.forward.z), new Vector2(Destination.forward.x, Destination.forward.z));
camera.transform.eulerAngles = playerRotation.eulerAngles + new Vector3(0, relativeAngle, 0);
camera.transform.position = Destination.position + Quaternion.Euler(0, relativeAngle, 0) * (playerPosition - transform.position);
// TODO: Near plane clipping.
camera.Render();
}
private static Matrix4x4 GetSteamVrProjectionMatrix(Camera cam, EVREye eye) {
var proj = SteamVR.instance.hmd.GetProjectionMatrix(eye, cam.nearClipPlane, cam.farClipPlane, SteamVR.instance.graphicsAPI);
return new Matrix4x4 {
m00 = proj.m0,
m01 = proj.m1,
m02 = proj.m2,
m03 = proj.m3,
m10 = proj.m4,
m11 = proj.m5,
m12 = proj.m6,
m13 = proj.m7,
m20 = proj.m8,
m21 = proj.m9,
m22 = proj.m10,
m23 = proj.m11,
m30 = proj.m12,
m31 = proj.m13,
m32 = proj.m14,
m33 = proj.m15
};
}
}
}
using System.Collections.Generic;
using UnityEngine;
namespace Assets.Scripts {
public class WorldGenerator : MonoBehaviour {
private static readonly Vector3 RoomScale = SteamVRHelper.GetBounds();
public Camera PlayerCamera;
public GameObject Portal;
public GameObject Room;
public Vector3 Size = new Vector3(3, 3, 3);
public void Start() {
Portal.GetComponent<Portal>().PlayerCamera = PlayerCamera;
Room.transform.FindChild("Walls").transform.localScale = RoomScale;
var rooms = new Dictionary<int, Dictionary<int, Dictionary<int, GameObject>>>();
for (var x = 0; x < Size.x; x++) {
for (var y = 0; y < Size.y; y++) {
for (var z = 0; z < Size.z; z++) {
var coordinate = new Vector3(2 * x, 2 * y, 2 * z);
var position = Vector3.Scale(RoomScale, coordinate);
Room.transform.position = position;
var color = new Color(x / Size.x, y / Size.y, z / Size.z);
var room = Instantiate(Room);
room.name = string.Format("Room ({0}, {1}, {2})", x, y, z);
foreach (var wallRenderer in room.transform.FindChild("Walls").GetComponentsInChildren<Renderer>()) {
wallRenderer.material = wallRenderer.sharedMaterial;
wallRenderer.material.color = color;
}
var scale = RoomScale;
var xOffset = (scale.x - Portal.transform.localScale.x) / 2;
var yOffset = scale.y / 2;
var zOffset = 0;
var portals = room.transform.FindChild("Portals");
var portalOffset = position;
var portalScale = Portal.transform.localScale;
Portal.transform.localScale = new Vector3(portalScale.x, scale.y, portalScale.z);
var portalRotation = 0;
Portal.transform.eulerAngles = new Vector3(0, portalRotation, 0);
Portal.transform.position = new Vector3(-xOffset, yOffset, -zOffset) + portalOffset;
if (x != 0) {
var leftPortal = Instantiate(Portal);
leftPortal.name = "Left Portal";
leftPortal.transform.parent = portals;
}
portalRotation += 180;
Portal.transform.eulerAngles = new Vector3(0, portalRotation, 0);
Portal.transform.position = new Vector3(-xOffset, yOffset, zOffset) + portalOffset;
if (x != Size.x - 1) {
var rightPortal = Instantiate(Portal);
rightPortal.name = "Right Portal";
rightPortal.transform.parent = portals;
}
Portal.transform.eulerAngles = new Vector3(0, portalRotation, 0);
Portal.transform.position = new Vector3(xOffset, yOffset, zOffset) + portalOffset;
if (z != Size.z - 1) {
var frontPortal = Instantiate(Portal);
frontPortal.name = "Front Portal";
frontPortal.transform.parent = portals;
}
portalRotation += 180;
Portal.transform.eulerAngles = new Vector3(0, portalRotation, 0);
Portal.transform.position = new Vector3(xOffset, yOffset, -zOffset) + portalOffset;
if (z != 0) {
var backPortal = Instantiate(Portal);
backPortal.name = "Back Portal";
backPortal.transform.parent = portals;
}
var row = rooms.ContainsKey(x) ? rooms[x] : rooms[x] = new Dictionary<int, Dictionary<int, GameObject>>();
var column = row.ContainsKey(y) ? row[y] : row[y] = new Dictionary<int, GameObject>();
column[z] = room;
}
}
}
for (var x = 0; x < Size.x; x++) {
for (var y = 0; y < Size.y; y++) {
for (var z = 0; z < Size.z; z++) {
var portals = rooms[x][y][z].transform.FindChild("Portals");
var leftPortal = portals.FindChild("Left Portal");
var rightPortal = portals.FindChild("Right Portal");
var backPortal = portals.FindChild("Back Portal");
var frontPortal = portals.FindChild("Front Portal");
if (leftPortal != null) {
leftPortal.GetComponent<Portal>().Destination = rooms[x - 1][y][z].transform.FindChild("Portals").FindChild("Right Portal");
}
if (rightPortal != null) {
rightPortal.GetComponent<Portal>().Destination = rooms[x + 1][y][z].transform.FindChild("Portals").FindChild("Left Portal");
}
if (backPortal != null) {
backPortal.GetComponent<Portal>().Destination = rooms[x][y][z - 1].transform.FindChild("Portals").FindChild("Front Portal");
}
if (frontPortal != null) {
frontPortal.GetComponent<Portal>().Destination = rooms[x][y][z + 1].transform.FindChild("Portals").FindChild("Back Portal");
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment