SharedVRCStuff/PlaneCam/Runtime/PlaneCam.cs
2026-02-04 21:35:29 -05:00

145 lines
No EOL
4.8 KiB
C#

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace gay.lilyy.PlaneCam
{
public enum PlaneDirection
{
XPositive,
XNegative,
YPositive,
YNegative,
ZPositive,
ZNegative
}
[AddComponentMenu("LillithRosePup/Plane Cam")]
[RequireComponent(typeof(Camera))]
public class PlaneCam : MonoBehaviour, VRC.SDKBase.IEditorOnly
{
public GameObject target;
public PlaneDirection direction = PlaneDirection.ZPositive;
public void UpdateCamera()
{
if (target == null)
return;
Camera cam = GetComponent<Camera>();
if (cam == null)
return;
// Get the bounds of the target plane
Bounds bounds = GetBounds(target);
if (bounds.size.magnitude < 0.001f)
return;
// Get plane normal and center
Vector3 planeNormal = GetPlaneNormal(target, direction);
Vector3 planeCenter = bounds.center;
// Calculate the plane's dimensions in its local space
// Project bounds size onto the plane's local axes
Transform planeTransform = target.transform;
Vector3 localSize = bounds.size;
// Find the two dimensions that define the plane (ignore the smallest dimension)
// This assumes the plane is flat, so one dimension should be very small
float minDim = Mathf.Min(localSize.x, localSize.y, localSize.z);
float maxDim = Mathf.Max(localSize.x, localSize.y, localSize.z);
float midDim = localSize.x + localSize.y + localSize.z - minDim - maxDim;
// Use the larger of the two plane dimensions to ensure it fills the square viewport
float planeSize = Mathf.Max(maxDim, midDim);
// Set aspect ratio to 1:1 for square viewport
cam.aspect = 1.0f;
// Calculate distance and position
if (cam.orthographic)
{
cam.orthographicSize = planeSize * 0.5f;
// Position camera perpendicular to the plane
cam.transform.position = planeCenter - planeNormal * 10f; // Distance doesn't matter for orthographic
cam.transform.LookAt(planeCenter, planeTransform.up);
}
else
{
// For perspective camera, calculate distance based on FOV
// distance = (size/2) / tan(FOV/2)
float halfFOV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
float distance = (planeSize * 0.5f) / Mathf.Tan(halfFOV);
// Position camera perpendicular to the plane, looking at center
cam.transform.position = planeCenter - planeNormal * distance;
cam.transform.LookAt(planeCenter, planeTransform.up);
}
}
Bounds GetBounds(GameObject target)
{
Renderer renderer = target.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.bounds;
}
// If no renderer, try to get bounds from collider
Collider collider = target.GetComponent<Collider>();
if (collider != null)
{
return collider.bounds;
}
// Fallback: use transform scale
return new Bounds(target.transform.position, target.transform.lossyScale);
}
Vector3 GetPlaneNormal(GameObject target, PlaneDirection direction)
{
Transform t = target.transform;
// Use the specified direction setting
switch (direction)
{
case PlaneDirection.XPositive:
return t.right;
case PlaneDirection.XNegative:
return -t.right;
case PlaneDirection.YPositive:
return t.up;
case PlaneDirection.YNegative:
return -t.up;
case PlaneDirection.ZPositive:
return t.forward;
case PlaneDirection.ZNegative:
return -t.forward;
default:
return t.forward;
}
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(PlaneCam))]
public class PlaneCamInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
PlaneCam planeCam = (PlaneCam)target;
EditorGUILayout.Space();
if (GUILayout.Button("Update Camera"))
{
planeCam.UpdateCamera();
}
}
}
#endif
}