using UnityEngine; namespace Rokid.UXR.Interaction { public class PlaneSurface : MonoBehaviour, ISurface, IBounds { public enum NormalFacing { /// /// Normal faces along the transform's negative Z axis /// Backward, /// /// Normal faces along the transform's positive Z axis /// Forward, } [SerializeField] private NormalFacing _facing = NormalFacing.Backward; [SerializeField, Tooltip("Raycasts hit either side of plane, but hit normal " + "will still respect plane facing.")] private bool _doubleSided = false; public NormalFacing Facing { get => _facing; set => _facing = value; } public bool DoubleSided { get => _doubleSided; set => _doubleSided = value; } public Vector3 Normal { get { return _facing == NormalFacing.Forward ? transform.forward : -transform.forward; } } private bool IsPointAboveSurface(Vector3 point) { Plane plane = GetPlane(); return plane.GetSide(point); } public bool ClosestSurfacePoint(in Vector3 point, out SurfaceHit hit, float maxDistance) { hit = new SurfaceHit(); Plane plane = GetPlane(); float hitDistance = plane.GetDistanceToPoint(point); if (maxDistance > 0 && Mathf.Abs(hitDistance) > maxDistance) { return false; } hit.Point = plane.ClosestPointOnPlane(point); hit.Distance = IsPointAboveSurface(point) ? hitDistance : -hitDistance; hit.Normal = plane.normal; return true; } public Transform Transform => transform; public Bounds Bounds { get { Vector3 size = new Vector3( Mathf.Abs(Normal.x) == 1f ? float.Epsilon : float.PositiveInfinity, Mathf.Abs(Normal.y) == 1f ? float.Epsilon : float.PositiveInfinity, Mathf.Abs(Normal.z) == 1f ? float.Epsilon : float.PositiveInfinity); return new Bounds(transform.position, size); } } public bool Raycast(in Ray ray, out SurfaceHit hit, float maxDistance) { hit = new SurfaceHit(); Plane plane = GetPlane(); if (!_doubleSided && !IsPointAboveSurface(ray.origin)) { return false; } if (plane.Raycast(ray, out float hitDistance)) { if (maxDistance > 0 && hitDistance > maxDistance) { return false; } hit.Point = ray.GetPoint(hitDistance); hit.Normal = plane.normal; hit.Distance = hitDistance; return true; } return false; } public Plane GetPlane() { return new Plane(Normal, transform.position); } } }