314 lines
9.7 KiB
C#
314 lines
9.7 KiB
C#
/************************************************************************************
|
|
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
|
|
|
Licensed under the Oculus Utilities SDK License Version 1.31 (the "License"); you may not use
|
|
the Utilities SDK except in compliance with the License, which is provided at the time of installation
|
|
or download, or which otherwise accompanies this software in either electronic or hard copy form.
|
|
|
|
You may obtain a copy of the License at
|
|
https://developer.oculus.com/licenses/utilities-1.31
|
|
|
|
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
|
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
ANY KIND, either express or implied. See the License for the specific language governing
|
|
permissions and limitations under the License.
|
|
************************************************************************************/
|
|
//#define DEBUG_OVERLAY_CANVAS
|
|
using UnityEngine;
|
|
|
|
[RequireComponent(typeof(Canvas))]
|
|
public class OVROverlayCanvas : MonoBehaviour
|
|
{
|
|
[SerializeField, HideInInspector]
|
|
private Shader _transparentShader = null;
|
|
[SerializeField, HideInInspector]
|
|
private Shader _opaqueShader = null;
|
|
|
|
RectTransform _rectTransform;
|
|
Canvas _canvas;
|
|
Camera _camera;
|
|
OVROverlay _overlay;
|
|
RenderTexture _renderTexture;
|
|
MeshRenderer _meshRenderer;
|
|
|
|
Mesh _quad;
|
|
Material _defaultMat;
|
|
|
|
public int MaxTextureSize = 1600;
|
|
public int MinTextureSize = 200;
|
|
public float PixelsPerUnit = 1f;
|
|
public int DrawRate = 1;
|
|
public int DrawFrameOffset = 0;
|
|
public bool Expensive = false;
|
|
public int Layer = 0;
|
|
|
|
public enum DrawMode
|
|
{
|
|
Opaque,
|
|
OpaqueWithClip,
|
|
TransparentDefaultAlpha,
|
|
TransparentCorrectAlpha
|
|
}
|
|
|
|
|
|
public DrawMode Opacity = DrawMode.OpaqueWithClip;
|
|
|
|
private bool ScaleViewport = Application.isMobilePlatform;
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
_canvas = GetComponent<Canvas>();
|
|
|
|
_rectTransform = _canvas.GetComponent<RectTransform>();
|
|
|
|
float rectWidth = _rectTransform.rect.width;
|
|
float rectHeight = _rectTransform.rect.height;
|
|
|
|
float aspectX = rectWidth >= rectHeight ? 1 : rectWidth / rectHeight;
|
|
float aspectY = rectHeight >= rectWidth ? 1 : rectHeight / rectWidth;
|
|
|
|
// if we are scaling the viewport we don't need to add a border
|
|
int pixelBorder = ScaleViewport ? 0 : 8;
|
|
int innerWidth = Mathf.CeilToInt(aspectX * (MaxTextureSize - pixelBorder * 2));
|
|
int innerHeight = Mathf.CeilToInt(aspectY * (MaxTextureSize - pixelBorder * 2));
|
|
int width = innerWidth + pixelBorder * 2;
|
|
int height = innerHeight + pixelBorder * 2;
|
|
|
|
float paddedWidth = rectWidth * (width / (float)innerWidth);
|
|
float paddedHeight = rectHeight * (height / (float)innerHeight);
|
|
|
|
float insetRectWidth = innerWidth / (float)width;
|
|
float insetRectHeight = innerHeight / (float)height;
|
|
|
|
// ever so slightly shrink our opaque mesh to avoid black borders
|
|
Vector2 opaqueTrim = Opacity == DrawMode.Opaque ? new Vector2(0.005f / _rectTransform.lossyScale.x, 0.005f / _rectTransform.lossyScale.y) : Vector2.zero;
|
|
|
|
_renderTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
|
// if we can't scale the viewport, generate mipmaps instead
|
|
_renderTexture.useMipMap = !ScaleViewport;
|
|
|
|
GameObject overlayCamera = new GameObject(name + " Overlay Camera")
|
|
{
|
|
#if !DEBUG_OVERLAY_CANVAS
|
|
hideFlags = HideFlags.HideInHierarchy | HideFlags.NotEditable
|
|
#endif
|
|
};
|
|
overlayCamera.transform.SetParent(transform, false);
|
|
|
|
_camera = overlayCamera.AddComponent<Camera>();
|
|
_camera.stereoTargetEye = StereoTargetEyeMask.None;
|
|
_camera.transform.position = transform.position - transform.forward;
|
|
_camera.orthographic = true;
|
|
_camera.enabled = false;
|
|
_camera.targetTexture = _renderTexture;
|
|
_camera.cullingMask = 1 << gameObject.layer;
|
|
_camera.clearFlags = CameraClearFlags.SolidColor;
|
|
_camera.backgroundColor = Color.clear;
|
|
_camera.orthographicSize = 0.5f * paddedHeight * _rectTransform.localScale.y;
|
|
_camera.nearClipPlane = 0.99f;
|
|
_camera.farClipPlane = 1.01f;
|
|
|
|
_quad = new Mesh()
|
|
{
|
|
name = name + " Overlay Quad",
|
|
hideFlags = HideFlags.HideAndDontSave
|
|
};
|
|
|
|
_quad.vertices = new Vector3[] { new Vector3(-0.5f, -0.5f), new Vector3(-0.5f, 0.5f), new Vector3(0.5f, 0.5f), new Vector3(0.5f, -0.5f) };
|
|
_quad.uv = new Vector2[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0) };
|
|
_quad.triangles = new int[] { 0, 1, 2, 2, 3, 0 };
|
|
_quad.bounds = new Bounds(Vector3.zero, Vector3.one);
|
|
_quad.UploadMeshData(true);
|
|
|
|
switch(Opacity)
|
|
{
|
|
case DrawMode.Opaque:
|
|
_defaultMat = new Material(_opaqueShader);
|
|
break;
|
|
case DrawMode.OpaqueWithClip:
|
|
_defaultMat = new Material(_opaqueShader);
|
|
_defaultMat.EnableKeyword("WITH_CLIP");
|
|
break;
|
|
case DrawMode.TransparentDefaultAlpha:
|
|
_defaultMat = new Material(_transparentShader);
|
|
_defaultMat.EnableKeyword("ALPHA_SQUARED");
|
|
break;
|
|
case DrawMode.TransparentCorrectAlpha:
|
|
_defaultMat = new Material(_transparentShader);
|
|
break;
|
|
}
|
|
_defaultMat.mainTexture = _renderTexture;
|
|
_defaultMat.color = Color.black;
|
|
_defaultMat.mainTextureOffset = new Vector2(0.5f - 0.5f * insetRectWidth, 0.5f - 0.5f * insetRectHeight);
|
|
_defaultMat.mainTextureScale = new Vector2(insetRectWidth, insetRectHeight);
|
|
|
|
GameObject meshRenderer = new GameObject(name + " MeshRenderer")
|
|
{
|
|
#if !DEBUG_OVERLAY_CANVAS
|
|
hideFlags = HideFlags.HideInHierarchy | HideFlags.NotEditable
|
|
#endif
|
|
};
|
|
|
|
meshRenderer.transform.SetParent(transform, false);
|
|
meshRenderer.AddComponent<MeshFilter>().sharedMesh = _quad;
|
|
_meshRenderer = meshRenderer.AddComponent<MeshRenderer>();
|
|
_meshRenderer.sharedMaterial = _defaultMat;
|
|
meshRenderer.layer = Layer;
|
|
meshRenderer.transform.localScale = new Vector3(rectWidth - opaqueTrim.x, rectHeight - opaqueTrim.y, 1);
|
|
|
|
GameObject overlay = new GameObject(name + " Overlay")
|
|
{
|
|
#if !DEBUG_OVERLAY_CANVAS
|
|
hideFlags = HideFlags.HideInHierarchy | HideFlags.NotEditable
|
|
#endif
|
|
};
|
|
overlay.transform.SetParent(transform, false);
|
|
_overlay = overlay.AddComponent<OVROverlay>();
|
|
_overlay.isDynamic = true;
|
|
_overlay.noDepthBufferTesting = true;
|
|
_overlay.isAlphaPremultiplied = !Application.isMobilePlatform;
|
|
_overlay.textures[0] = _renderTexture;
|
|
_overlay.currentOverlayType = OVROverlay.OverlayType.Underlay;
|
|
_overlay.transform.localScale = new Vector3(paddedWidth, paddedHeight, 1);
|
|
_overlay.useExpensiveSuperSample = Expensive;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
Destroy(_defaultMat);
|
|
Destroy(_quad);
|
|
Destroy(_renderTexture);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (_overlay)
|
|
{
|
|
_meshRenderer.enabled = true;
|
|
_overlay.enabled = true;
|
|
}
|
|
if (_camera)
|
|
{
|
|
_camera.enabled = true;
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (_overlay)
|
|
{
|
|
_overlay.enabled = false;
|
|
_meshRenderer.enabled = false;
|
|
}
|
|
|
|
if (_camera)
|
|
{
|
|
_camera.enabled = false;
|
|
}
|
|
}
|
|
|
|
private static readonly Plane[] _FrustumPlanes = new Plane[6];
|
|
protected virtual bool ShouldRender()
|
|
{
|
|
if (DrawRate > 1)
|
|
{
|
|
if (Time.frameCount % DrawRate != DrawFrameOffset % DrawRate)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (Camera.main != null)
|
|
{
|
|
// Perform Frustum culling
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
var eye = (Camera.StereoscopicEye)i;
|
|
var mat = Camera.main.GetStereoProjectionMatrix(eye) * Camera.main.GetStereoViewMatrix(eye);
|
|
GeometryUtility.CalculateFrustumPlanes(mat, _FrustumPlanes);
|
|
if (GeometryUtility.TestPlanesAABB(_FrustumPlanes, _meshRenderer.bounds))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (ShouldRender())
|
|
{
|
|
if (ScaleViewport)
|
|
{
|
|
if (Camera.main != null)
|
|
{
|
|
float d = (Camera.main.transform.position - transform.position).magnitude;
|
|
|
|
float size = PixelsPerUnit * Mathf.Max(_rectTransform.rect.width * transform.lossyScale.x, _rectTransform.rect.height * transform.lossyScale.y) / d;
|
|
|
|
// quantize to even pixel sizes
|
|
const float quantize = 8;
|
|
float pixelHeight = Mathf.Ceil(size / quantize * _renderTexture.height) * quantize;
|
|
|
|
// clamp between or min size and our max size
|
|
pixelHeight = Mathf.Clamp(pixelHeight, MinTextureSize, _renderTexture.height);
|
|
|
|
float innerPixelHeight = pixelHeight - 2;
|
|
|
|
_camera.orthographicSize = 0.5f * _rectTransform.rect.height * _rectTransform.localScale.y * pixelHeight / innerPixelHeight;
|
|
|
|
float aspect = (_rectTransform.rect.width / _rectTransform.rect.height);
|
|
|
|
float innerPixelWidth = innerPixelHeight * aspect;
|
|
float pixelWidth = Mathf.Ceil((innerPixelWidth + 2) * 0.5f) * 2;
|
|
|
|
float sizeX = pixelWidth / _renderTexture.width;
|
|
float sizeY = pixelHeight / _renderTexture.height;
|
|
|
|
// trim a half pixel off each size if this is opaque (transparent should fade)
|
|
float inset = Opacity == DrawMode.Opaque ? 1.001f : 0;
|
|
|
|
float innerSizeX = (innerPixelWidth - inset) / _renderTexture.width;
|
|
float innerSizeY = (innerPixelHeight - inset) / _renderTexture.height;
|
|
|
|
// scale the camera rect
|
|
_camera.rect = new Rect((1 - sizeX) / 2, (1 - sizeY) / 2, sizeX, sizeY);
|
|
|
|
Rect src = new Rect(0.5f - (0.5f * innerSizeX), 0.5f - (0.5f * innerSizeY), innerSizeX, innerSizeY);
|
|
|
|
_defaultMat.mainTextureOffset = src.min;
|
|
_defaultMat.mainTextureScale = src.size;
|
|
|
|
// update the overlay to use this same size
|
|
_overlay.overrideTextureRectMatrix = true;
|
|
src.y = 1 - src.height - src.y;
|
|
Rect dst = new Rect(0, 0, 1, 1);
|
|
_overlay.SetSrcDestRects(src, src, dst, dst);
|
|
}
|
|
}
|
|
|
|
_camera.Render();
|
|
}
|
|
}
|
|
|
|
public bool overlayEnabled
|
|
{
|
|
get
|
|
{
|
|
return _overlay && _overlay.enabled;
|
|
}
|
|
set
|
|
{
|
|
if (_overlay)
|
|
{
|
|
_overlay.enabled = value;
|
|
_defaultMat.color = value ? Color.black : Color.white;
|
|
}
|
|
}
|
|
}
|
|
}
|