using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Profiling;
namespace Rokid.UXR.Interaction
{
using OverlayType = RKOverlay.OverlayType;
using OverlayShape = RKOverlay.OverlayShape;
public enum RKRenderingMode
{
[InspectorName("Alpha-Blended")]
AlphaBlended = RenderingMode.AlphaBlended,
[InspectorName("Alpha-Cutout")]
AlphaCutout = RenderingMode.AlphaCutout,
[InspectorName("Opaque")]
Opaque = RenderingMode.Opaque,
[InspectorName("RK/Overlay")]
Overlay = 100,
[InspectorName("RK/Underlay")]
Underlay,
}
///
/// Uses to enable Underlay and Overlay
/// rendering of a UI canvas.
///
public class RKCanvasMeshRenderer : CanvasMeshRenderer
{
[SerializeField]
protected CanvasMesh _canvasMesh;
[Tooltip("If non-zero it will cause the position of the overlay to be offset by this amount at runtime, while " +
"the renderer will remain where it was at edit time. This can be used to prevent the two representations from overlapping.")]
[SerializeField]
protected Vector3 _runtimeOffset = new Vector3(0, 0, 0);
[Tooltip(
"Uses a more expensive image sampling technique for improved quality at the cost of performance.")]
[SerializeField]
protected bool _enableSuperSampling = true;
[Tooltip(
"Attempts to anti-alias the edges of the underlay by using alpha blending. Can cause borders of " +
"darkness around partially transparent objects.")]
[SerializeField]
private bool _doUnderlayAntiAliasing = false;
private bool _emulateWhileInEditor = true;
protected RKOverlay _overlay;
private RKRenderingMode RenderingMode => (RKRenderingMode)_renderingMode;
public bool ShouldUseOVROverlay
{
get
{
switch (RenderingMode)
{
case RKRenderingMode.Underlay:
case RKRenderingMode.Overlay:
return !UseEditorEmulation();
default:
return false;
}
}
}
protected override string GetShaderName()
{
switch (RenderingMode)
{
case RKRenderingMode.Overlay:
return "Hidden/Imposter_AlphaCutout";
case RKRenderingMode.Underlay:
if (UseEditorEmulation())
{
return "Hidden/Imposter_AlphaCutout";
}
else if (_doUnderlayAntiAliasing)
{
return "Hidden/Imposter_Underlay_AA";
}
else
{
return "Hidden/Imposter_Underlay";
}
default:
return base.GetShaderName();
}
}
protected override float GetAlphaCutoutThreshold()
{
switch (RenderingMode)
{
case RKRenderingMode.Overlay:
return 1f;
case RKRenderingMode.Underlay:
return UseEditorEmulation() ? 0.5f : 1f;
default:
return base.GetAlphaCutoutThreshold();
}
}
protected override void HandleUpdateRenderTexture(Texture texture)
{
base.HandleUpdateRenderTexture(texture);
UpdateOverlay(texture);
}
private bool UseEditorEmulation()
{
return Application.isEditor ? _emulateWhileInEditor : false;
}
private bool GetOverlayParameters(out OverlayShape shape,
out Vector3 position,
out Vector3 scale)
{
if (_canvasMesh is CanvasCylinder canvasCylinder)
{
shape = OverlayShape.Cylinder;
Vector2Int resolution = _canvasRenderTexture.GetBaseResolutionToUse();
position = new Vector3(0, 0, -canvasCylinder.Radius) - _runtimeOffset;
scale = new Vector3(_canvasRenderTexture.PixelsToUnits(resolution.x) /
canvasCylinder.transform.lossyScale.x,
_canvasRenderTexture.PixelsToUnits(resolution.y) /
canvasCylinder.transform.lossyScale.y,
canvasCylinder.Radius);
return true;
}
else if (_canvasMesh is CanvasRect canvasRect)
{
shape = OverlayShape.Quad;
Vector2Int resolution = _canvasRenderTexture.GetBaseResolutionToUse();
position = -_runtimeOffset;
scale = new Vector3(_canvasRenderTexture.PixelsToUnits(resolution.x),
_canvasRenderTexture.PixelsToUnits(resolution.y),
1);
return true;
}
else // Unsupported
{
shape = OverlayShape.Quad;
position = Vector3.zero;
scale = Vector3.zero;
return false;
}
}
protected override void Start()
{
this.BeginStart(ref _started, () => base.Start());
Assert.IsNotNull(_canvasMesh);
Assert.IsTrue(GetOverlayParameters(out _, out _, out _),
$"Unsupported {nameof(CanvasMesh)} type");
this.EndStart(ref _started);
}
protected void UpdateOverlay(Texture texture)
{
Profiler.BeginSample("InterfaceRenderer.UpdateOverlay");
try
{
if (!ShouldUseOVROverlay)
{
_overlay?.gameObject?.SetActive(false);
return;
}
if (_overlay == null)
{
GameObject overlayObj = CreateChildObject("__Overlay");
_overlay = overlayObj.AddComponent();
_overlay.isAlphaPremultiplied = !Application.isMobilePlatform;
}
else
{
_overlay.gameObject.SetActive(true);
}
if (!GetOverlayParameters(out OverlayShape shape,
out Vector3 pos,
out Vector3 scale))
{
_overlay.gameObject.SetActive(false);
return;
}
bool useUnderlayRendering = RenderingMode == RKRenderingMode.Underlay;
_overlay.textures = new Texture[1] { texture };
_overlay.noDepthBufferTesting = useUnderlayRendering;
_overlay.currentOverlayType = useUnderlayRendering ? OverlayType.Underlay : OverlayType.Overlay;
_overlay.currentOverlayShape = shape;
_overlay.useExpensiveSuperSample = _enableSuperSampling;
_overlay.transform.localPosition = pos;
_overlay.transform.localScale = scale;
}
finally
{
Profiler.EndSample();
}
}
protected GameObject CreateChildObject(string name)
{
GameObject obj = new GameObject(name);
obj.transform.SetParent(transform);
obj.transform.localPosition = Vector3.zero;
obj.transform.localRotation = Quaternion.identity;
obj.transform.localScale = Vector3.one;
return obj;
}
public static new class Properties
{
public static readonly string CanvasRenderTexture = nameof(_canvasRenderTexture);
public static readonly string CanvasMesh = nameof(_canvasMesh);
public static readonly string EnableSuperSampling = nameof(_enableSuperSampling);
public static readonly string EmulateWhileInEditor = nameof(_emulateWhileInEditor);
public static readonly string DoUnderlayAntiAliasing = nameof(_doUnderlayAntiAliasing);
public static readonly string RuntimeOffset = nameof(_runtimeOffset);
}
}
}