Files
modular-vr/Assets/Portal/Runtime/RenderPortalCameras.cs
2023-03-15 13:47:11 +01:00

124 lines
5.8 KiB
C#

using System.Collections.Generic;
using EscapeRoomEngine.VR.Runtime;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace EscapeRoomEngine.Portal.Runtime
{
public class RenderPortalCameras : ScriptableRendererFeature
{
private enum PassType
{
Opaque,
Transparent
}
private class PortalCameraRenderPass : ScriptableRenderPass
{
private static readonly List<ShaderTagId> ShaderTagIds = new()
{
new ShaderTagId("SRPDefaultUnlit"), new ShaderTagId("UniversalForward"), new ShaderTagId("UniversalForwardOnly")
};
private RenderStateBlock _renderStateBlock;
private CommandBuffer _commandBuffer;
private readonly LayerMask _layers;
private readonly PassType _passType;
public PortalCameraRenderPass(LayerMask layers, PassType passType)
{
_layers = layers;
_passType = passType;
renderPassEvent = passType == PassType.Opaque ? RenderPassEvent.AfterRenderingOpaques : RenderPassEvent.BeforeRenderingTransparents;
_renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
// set stencil
var stencilState = StencilState.defaultValue;
stencilState.enabled = true;
stencilState.SetCompareFunction(CompareFunction.Equal);
stencilState.SetPassOperation(StencilOp.Keep);
stencilState.SetFailOperation(StencilOp.Keep);
stencilState.SetZFailOperation(StencilOp.Keep);
_renderStateBlock.mask |= RenderStateMask.Stencil;
_renderStateBlock.stencilState = stencilState;
}
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
// we need to store a reference to the command buffer before executing, because the reference in RenderingData is internal
_commandBuffer = cmd;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (Player.Instance)
{
var drawingSettings = CreateDrawingSettings(ShaderTagIds, ref renderingData, _passType == PassType.Opaque ? renderingData.cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent);
var filteringSettings = new FilteringSettings(_passType == PassType.Opaque ? RenderQueueRange.opaque : RenderQueueRange.transparent, _layers);
foreach (var portal in FindObjectsByType<Portal>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{
var linkedPortal = portal.linkedPortal;
if (linkedPortal)
{
// set which portal to render
_renderStateBlock.stencilReference = portal.PortalNumber;
// prepare the camera
ref var cameraData = ref renderingData.cameraData;
var camera = cameraData.camera;
var cameraTransform = camera.transform;
var originalPosition = cameraTransform.position;
var originalRotation = cameraTransform.rotation;
var originalProjection = camera.projectionMatrix;
camera = linkedPortal.SetUpCamera(camera,
camera.stereoEnabled
? cameraData.xr.multipassId == 0
? Camera.MonoOrStereoscopicEye.Left
: Camera.MonoOrStereoscopicEye.Right
: Camera.MonoOrStereoscopicEye.Mono);
RenderingUtils.SetViewAndProjectionMatrices(_commandBuffer,
camera.worldToCameraMatrix,
GL.GetGPUProjectionMatrix(camera.projectionMatrix, true), false);
// execute command buffer
context.ExecuteCommandBuffer(_commandBuffer);
_commandBuffer.Clear();
// frustum culling
camera.TryGetCullingParameters(true, out var cullingParameters);
var cullingResults = context.Cull(ref cullingParameters);
// render the portal
context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings, ref _renderStateBlock);
// reset
RenderingUtils.SetViewAndProjectionMatrices(_commandBuffer, cameraData.GetViewMatrix(), cameraData.GetGPUProjectionMatrix(), false);
camera.transform.SetPositionAndRotation(originalPosition, originalRotation);
camera.projectionMatrix = originalProjection;
}
}
}
}
}
private PortalCameraRenderPass _opaquePass, _transparentPass;
public LayerMask layers;
public override void Create()
{
_opaquePass = new PortalCameraRenderPass(layers, PassType.Opaque);
_transparentPass = new PortalCameraRenderPass(layers, PassType.Transparent);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(_opaquePass);
renderer.EnqueuePass(_transparentPass);
}
}
}