129 lines
4.6 KiB
C#
129 lines
4.6 KiB
C#
using UnityEngine;
|
|
|
|
namespace Unheimlich.Utility
|
|
{
|
|
/// <summary>
|
|
/// Utility functions for portal camera rendering and visibility calculations.
|
|
/// Used to optimize portal rendering by detecting visibility and overlaps.
|
|
/// </summary>
|
|
public static class PortalCameraUtility
|
|
{
|
|
private 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),
|
|
};
|
|
|
|
/// <summary>
|
|
/// Tests if a renderer is visible from the camera's perspective.
|
|
/// Uses frustum culling to determine visibility.
|
|
/// </summary>
|
|
public static bool VisibleFromCamera(Renderer renderer, Camera camera)
|
|
{
|
|
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(camera);
|
|
return GeometryUtility.TestPlanesAABB(frustumPlanes, renderer.bounds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if two objects' screen-space bounding boxes overlap when viewed from a camera.
|
|
/// Used to determine if one portal is being viewed through another portal (recursion).
|
|
/// Returns true if the far object is visible through/behind the near object.
|
|
/// </summary>
|
|
public static bool BoundsOverlap(MeshFilter nearObject, MeshFilter farObject, Camera camera)
|
|
{
|
|
var near = GetScreenRectFromBounds(nearObject, camera);
|
|
var far = GetScreenRectFromBounds(farObject, camera);
|
|
|
|
if (far.zMax > near.zMin)
|
|
{
|
|
if (far.xMax < near.xMin || far.xMin > near.xMax)
|
|
return false;
|
|
|
|
if (far.yMax < near.yMin || far.yMin > near.yMax)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a mesh's 3D bounding box to screen-space coordinates (viewport space).
|
|
/// Handles corner cases where parts of the mesh are behind the camera.
|
|
/// Returns a MinMax3D struct containing the min/max viewport coordinates (x, y) and depth (z).
|
|
/// </summary>
|
|
public static MinMax3D GetScreenRectFromBounds(MeshFilter renderer, Camera mainCamera)
|
|
{
|
|
MinMax3D minMax = new MinMax3D(float.MaxValue, float.MinValue);
|
|
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
|
|
{
|
|
viewportSpaceCorner.x = (viewportSpaceCorner.x <= 0.5f) ? 1 : 0;
|
|
viewportSpaceCorner.y = (viewportSpaceCorner.y <= 0.5f) ? 1 : 0;
|
|
}
|
|
|
|
minMax.AddPoint(viewportSpaceCorner);
|
|
}
|
|
|
|
if (!anyPointIsInFrontOfCamera)
|
|
{
|
|
return new MinMax3D();
|
|
}
|
|
|
|
return minMax;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a 3D axis-aligned bounding box in viewport/screen space.
|
|
/// X and Y are in viewport coordinates [0,1], Z is depth from camera.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|