VR4RoboticArm2/VR4RoboticArm/Library/PackageCache/com.meta.xr.sdk.interaction/Runtime/Scripts/Interaction/Surfaces/ClippedCylinderSurface.cs
IonutMocanu 48cccc22ad Main2
2025-09-08 11:13:29 +03:00

233 lines
8.1 KiB
C#

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
namespace Oculus.Interaction.Surfaces
{
/// <summary>
/// Used to manually clip a <cref="CylinderSurface" /> to the desired size.
/// </summary>
public class ClippedCylinderSurface : MonoBehaviour, IClippedSurface<ICylinderClipper>
{
private const float EPSILON = 0.0001f;
[Tooltip("The Cylinder Surface to be clipped.")]
[SerializeField]
private CylinderSurface _cylinderSurface;
[Tooltip("The clippers that will be used to clip the Cylinder Surface.")]
[SerializeField, Interface(typeof(ICylinderClipper))]
private List<UnityEngine.Object> _clippers = new List<UnityEngine.Object>();
private List<ICylinderClipper> Clippers { get; set; }
public Transform Transform => _cylinderSurface.Transform;
public ISurface BackingSurface => _cylinderSurface;
public Cylinder Cylinder => _cylinderSurface.Cylinder;
public IReadOnlyList<ICylinderClipper> GetClippers()
{
if (Clippers != null)
{
return Clippers;
}
else
{
return _clippers.ConvertAll(clipper => clipper as ICylinderClipper);
}
}
public bool Raycast(in Ray ray, out SurfaceHit hit, float maxDistance = 0)
{
return BackingSurface.Raycast(ray, out hit, maxDistance) &&
ClosestSurfacePoint(hit.Point, out SurfaceHit clamped, 0) &&
hit.Point.Approximately(clamped.Point, EPSILON);
}
protected virtual void Awake()
{
Clippers = _clippers.ConvertAll(clipper => clipper as ICylinderClipper);
}
protected virtual void Start()
{
this.AssertField(_cylinderSurface, nameof(_cylinderSurface));
this.AssertCollectionItems(Clippers, nameof(Clippers));
}
public bool GetClipped(out CylinderSegment clipped)
{
bool anyClipped = false;
bool isInfiniteHeight = true;
float minDeg = float.MinValue;
float maxDeg = float.MaxValue;
float bottom = float.MinValue;
float top = float.MaxValue;
IReadOnlyList<ICylinderClipper> clippers = GetClippers();
for (int i = 0; i < clippers.Count; i++)
{
ICylinderClipper clipper = clippers[i];
if (clipper == null ||
!clipper.GetCylinderSegment(out CylinderSegment segment))
{
continue;
}
anyClipped = true;
float clipMin = segment.Rotation - segment.ArcDegrees / 2f;
float clipMax = segment.Rotation + segment.ArcDegrees / 2f;
minDeg = Mathf.Max(minDeg, clipMin);
maxDeg = Mathf.Min(maxDeg, clipMax);
if (!segment.IsInfiniteHeight)
{
isInfiniteHeight = false;
bottom = Mathf.Max(bottom, segment.Bottom);
top = Mathf.Min(top, segment.Top);
}
}
if (!anyClipped)
{
clipped = CylinderSegment.Infinite();
return true;
}
if (minDeg > maxDeg || (!isInfiniteHeight && bottom > top))
{
clipped = default;
return false;
}
float center = Mathf.Lerp(minDeg, maxDeg, 0.5f) % 360f;
if (isInfiniteHeight)
{
bottom = 1;
top = -1;
}
clipped = new CylinderSegment(
center, maxDeg - minDeg, bottom, top);
return true;
}
public bool ClosestSurfacePoint(in Vector3 point, out SurfaceHit hit, float maxDistance = 0)
{
if (!GetClipped(out CylinderSegment clipped))
{
hit = default;
return false;
}
Vector3 localPoint = Cylinder.transform.InverseTransformPoint(point);
Vector3 localHitPoint = localPoint;
Vector3 nearestPointOnCenterAxis;
if (!clipped.IsInfiniteHeight)
{
localHitPoint.y = Mathf.Clamp(localHitPoint.y, clipped.Bottom, clipped.Top);
}
if (!clipped.IsInfiniteArc)
{
float angle = Mathf.Atan2(localHitPoint.x, localHitPoint.z) * Mathf.Rad2Deg % 360;
float rotation = clipped.Rotation % 360;
if (angle > rotation + 180)
{
angle -= 360;
}
else if (angle < rotation - 180)
{
angle += 360;
}
angle = Mathf.Clamp(angle, rotation - clipped.ArcDegrees / 2f,
rotation + clipped.ArcDegrees / 2f);
localHitPoint.x = Mathf.Sin(angle * Mathf.Deg2Rad) * Cylinder.Radius;
localHitPoint.z = Mathf.Cos(angle * Mathf.Deg2Rad) * Cylinder.Radius;
nearestPointOnCenterAxis = new Vector3(0f, localHitPoint.y, 0f);
}
else
{
nearestPointOnCenterAxis = new Vector3(0f, localHitPoint.y, 0f);
float distanceFromCenterAxis = Vector3.Distance(localHitPoint,
nearestPointOnCenterAxis);
localHitPoint = Vector3.MoveTowards(localHitPoint,
nearestPointOnCenterAxis,
distanceFromCenterAxis - Cylinder.Radius);
}
bool isOutside = (localPoint - nearestPointOnCenterAxis).magnitude > Cylinder.Radius;
Vector3 normal = (nearestPointOnCenterAxis - localHitPoint).normalized;
switch (_cylinderSurface.Facing)
{
default:
case CylinderSurface.NormalFacing.Any:
normal = isOutside ? -normal : normal;
break;
case CylinderSurface.NormalFacing.In:
break;
case CylinderSurface.NormalFacing.Out:
normal = -normal;
break;
}
hit = new SurfaceHit();
hit.Point = Cylinder.transform.TransformPoint(localHitPoint);
hit.Distance = Vector3.Distance(point, hit.Point);
hit.Normal = Cylinder.transform.TransformDirection(normal).normalized;
return maxDistance <= 0 || hit.Distance <= maxDistance;
}
#region Inject
public void InjectAllClippedCylinderSurface(CylinderSurface surface,
IEnumerable<ICylinderClipper> clippers)
{
InjectCylinderSurface(surface);
InjectClippers(clippers);
}
public void InjectCylinderSurface(CylinderSurface surface)
{
_cylinderSurface = surface;
}
public void InjectClippers(IEnumerable<ICylinderClipper> clippers)
{
_clippers = new List<UnityEngine.Object>(
clippers.Select(c => c as UnityEngine.Object));
Clippers = clippers.ToList();
}
#endregion
}
}