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

362 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction
{
/// <summary>
/// Canonical implementation of <see cref="IPointableElement"/>, this is a base type for components which represent something users can
/// interact with by "pointing" at it. As such, a PointableElement will receive, handles, and propagates (i.e., re-emits)
/// <see cref="PointerEvent"/>s generated by interactors pointing at the instance (typically
/// <see cref="PointerInteractor{TInteractor, TInteractable}"/>s).
/// </summary>
/// <remarks>
/// The Interaction SDK contains many examples of PointableElements, but one of the most versatile and important is the
/// <see cref="PointableCanvas"/>, which allows a Unity Canvas (which can contain conventional 2D Unity UI) to be targed by
/// Interaction SDK pointer interactions.
/// </remarks>
public class PointableElement : MonoBehaviour, IPointableElement
{
/// <summary>
/// If checked, if youre selecting an object with one hand and then select it with the other hand, the original hand is forced to release the object.
/// </summary>
[Tooltip("If checked, if youre selecting an object with one hand and then select it with the other hand, the original hand is forced to release the object.")]
[SerializeField]
private bool _transferOnSecondSelection;
/// <summary>
/// If checked, when you select an object, that hands Vector3 points are added to the beginning of the list of Vector3 points instead of the end.
/// This property has very unique usecases, so in most cases you should use the Transfer on Second Selection property instead.
/// </summary>
[Tooltip("If checked, when you select an object, that hands Vector3 points are added to the beginning of the list of Vector3 points instead of the end. This property has very unique usecases, so in most cases you should use the Transfer on Second Selection property instead.")]
[SerializeField]
private bool _addNewPointsToFront = false;
/// <summary>
/// Events will be forwarded to this element. Can be used to chain multiple PointableElements together. However, we recommend using the Interactable's Forward Element field instead.
/// </summary>
[Tooltip("Events will be forwarded to this element. Can be used to chain multiple PointableElements together. However, we recommend using the Interactable's Forward Element field instead.")]
[SerializeField, Interface(typeof(IPointableElement)), Optional]
private UnityEngine.Object _forwardElement;
/// <summary>
/// Optional <see cref="IPointableElement"/> to which this element will automatically forward its <see cref="PointerEvent"/>s. This
/// is equivalent to programmatically adding that element's <see cref="IPointableElement.ProcessPointerEvent(PointerEvent)"/>
/// method as a handler to this element's <see cref="WhenPointerEventRaised"/> event; this property simplyl allows that relationship
/// to be set up in the Unity Editor.
/// </summary>
public IPointableElement ForwardElement { get; private set; }
#region Properties
/// <summary>
/// If checked, if youre selecting an object with one hand and then select it with the other hand, the original hand is forced
/// to release the object.
/// </summary>
public bool TransferOnSecondSelection
{
get
{
return _transferOnSecondSelection;
}
set
{
_transferOnSecondSelection = value;
}
}
/// <summary>
/// If checked, when you select an object, that hands Vector3 points are added to the beginning of the list of Vector3 points
/// instead of the end. This property has very unique use cases, so in most cases you should use
/// <see cref="TransferOnSecondSelection"/> instead.
/// </summary>
public bool AddNewPointsToFront
{
get
{
return _addNewPointsToFront;
}
set
{
_addNewPointsToFront = value;
}
}
#endregion
/// <summary>
/// Implementation of <see cref="IPointable.WhenPointerEventRaised"/>; for details, please refer to the related
/// documentation provided for that interface.
/// </summary>
public event Action<PointerEvent> WhenPointerEventRaised = delegate { };
/// <summary>
/// The set of all points (positions and orientations) to which anything is currently "pointing" without selecting; i.e., the
/// set of locations at which hovering interactions currently exist.
/// </summary>
public List<Pose> Points => _points;
/// <summary>
/// Convenience method for counting the number of elements in <see cref="Points"/>; equivalent to calling `Points.Count`.
/// </summary>
public int PointsCount => _points.Count;
/// <summary>
/// The set of all points (positions and orientations) to which anything is currently "pointing" while selecting; i.e., the
/// set of locations at which select interactions currently exist.
/// </summary>
public List<Pose> SelectingPoints => _selectingPoints;
/// <summary>
/// Convenience method for counting the number of elements in <see cref="SelectingPoints"/>; equivalent to calling
/// `SelectingPoints.Count`.
/// </summary>
public int SelectingPointsCount => _selectingPoints.Count;
protected List<Pose> _points;
protected List<int> _pointIds;
protected List<Pose> _selectingPoints;
protected List<int> _selectingPointIds;
protected bool _started = false;
protected virtual void Awake()
{
ForwardElement = _forwardElement as IPointableElement;
_points = new List<Pose>();
_pointIds = new List<int>();
_selectingPoints = new List<Pose>();
_selectingPointIds = new List<int>();
}
protected virtual void Start()
{
this.BeginStart(ref _started);
if (_forwardElement)
{
this.AssertField(ForwardElement, nameof(ForwardElement));
}
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
if (ForwardElement != null)
{
ForwardElement.WhenPointerEventRaised += HandlePointerEventRaised;
}
}
}
protected virtual void OnDisable()
{
if (_started)
{
while (_selectingPoints.Count > 0)
{
Cancel(new PointerEvent(_selectingPointIds[0], PointerEventType.Cancel, _selectingPoints[0]));
}
if (ForwardElement != null)
{
ForwardElement.WhenPointerEventRaised -= HandlePointerEventRaised;
}
}
}
private void HandlePointerEventRaised(PointerEvent evt)
{
if (evt.Type == PointerEventType.Cancel)
{
ProcessPointerEvent(evt);
}
}
/// <summary>
/// Implementation of <see cref="IPointableElement.ProcessPointerEvent(PointerEvent)"/>; for details, please refer to the
/// related documentation provided for that interface.
/// </summary>
public virtual void ProcessPointerEvent(PointerEvent evt)
{
switch (evt.Type)
{
case PointerEventType.Hover:
Hover(evt);
break;
case PointerEventType.Unhover:
Unhover(evt);
break;
case PointerEventType.Move:
Move(evt);
break;
case PointerEventType.Select:
Select(evt);
break;
case PointerEventType.Unselect:
Unselect(evt);
break;
case PointerEventType.Cancel:
Cancel(evt);
break;
}
}
private void Hover(PointerEvent evt)
{
if (_addNewPointsToFront)
{
_pointIds.Insert(0, evt.Identifier);
_points.Insert(0, evt.Pose);
}
else
{
_pointIds.Add(evt.Identifier);
_points.Add(evt.Pose);
}
PointableElementUpdated(evt);
}
private void Move(PointerEvent evt)
{
int index = _pointIds.IndexOf(evt.Identifier);
if (index == -1)
{
return;
}
_points[index] = evt.Pose;
index = _selectingPointIds.IndexOf(evt.Identifier);
if (index != -1)
{
_selectingPoints[index] = evt.Pose;
}
PointableElementUpdated(evt);
}
private void Unhover(PointerEvent evt)
{
int index = _pointIds.IndexOf(evt.Identifier);
if (index == -1)
{
return;
}
_pointIds.RemoveAt(index);
_points.RemoveAt(index);
PointableElementUpdated(evt);
}
private void Select(PointerEvent evt)
{
if (_selectingPoints.Count == 1 && _transferOnSecondSelection)
{
Cancel(new PointerEvent(_selectingPointIds[0], PointerEventType.Cancel, _selectingPoints[0]));
}
if (_addNewPointsToFront)
{
_selectingPointIds.Insert(0, evt.Identifier);
_selectingPoints.Insert(0, evt.Pose);
}
else
{
_selectingPointIds.Add(evt.Identifier);
_selectingPoints.Add(evt.Pose);
}
PointableElementUpdated(evt);
}
private void Unselect(PointerEvent evt)
{
int index = _selectingPointIds.IndexOf(evt.Identifier);
if (index == -1)
{
return;
}
_selectingPointIds.RemoveAt(index);
_selectingPoints.RemoveAt(index);
PointableElementUpdated(evt);
}
private void Cancel(PointerEvent evt)
{
int index = _selectingPointIds.IndexOf(evt.Identifier);
if (index != -1)
{
_selectingPointIds.RemoveAt(index);
_selectingPoints.RemoveAt(index);
}
index = _pointIds.IndexOf(evt.Identifier);
if (index != -1)
{
_pointIds.RemoveAt(index);
_points.RemoveAt(index);
}
else
{
return;
}
PointableElementUpdated(evt);
}
protected virtual void PointableElementUpdated(PointerEvent evt)
{
if (ForwardElement != null)
{
ForwardElement.ProcessPointerEvent(evt);
}
WhenPointerEventRaised.Invoke(evt);
}
#region Inject
/// <summary>
/// Sets an <see cref="IPointableElement"/> to which a dynamically instantiated PointableElement should forward its
/// <see cref="PointerEvent"/>s. This method exists to support Interaction SDK's dependency injection pattern and is not
/// needed for typical Unity Editor-based usage.
/// </summary>
public void InjectOptionalForwardElement(IPointableElement forwardElement)
{
ForwardElement = forwardElement;
_forwardElement = forwardElement as UnityEngine.Object;
}
#endregion
}
}