mixedreality/com.microsoft.mixedreality..../SDK/Features/Input/Handlers/Constraints/ConstraintManager.cs

168 lines
7.1 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System.Collections.Generic;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.UI
{
/// <summary>
/// Manages constraints for a given object and ensures that Scale/Rotation/Translation
/// constraints are executed separately.
/// </summary>
[HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/ux-building-blocks/constraint-manager")]
public class ConstraintManager : MonoBehaviour
{
[SerializeField]
[Tooltip("Per default, constraint manager will apply all to this gameobject attached constraint components." +
"If this flag is enabled only the selected constraint list will be applied.")]
private bool autoConstraintSelection = true;
/// <summary>
/// Per default, constraint manager will apply all to this gameobject attached constraint components automatically.
/// If this flag is enabled, only the selected constraint list will be applied.
/// </summary>
public bool AutoConstraintSelection
{
get => autoConstraintSelection;
set => autoConstraintSelection = value;
}
[SerializeField]
[Tooltip("Manually selected list of transform constraints. Note that this list will only be processed by the" +
"manager if AutoConstraintSelection is disabled.")]
private List<TransformConstraint> selectedConstraints = new List<TransformConstraint>();
/// <summary>
/// Manually selected list of transform constraints. Note that this list will only be processed by the
/// manager if AutoConstraintSelection is disabled.
/// Note that this is a read only property. To add new constraints to the list call RegisterConstraint.
/// </summary>
public List<TransformConstraint> SelectedConstraints
{
get => selectedConstraints;
}
private List<TransformConstraint> constraints = new List<TransformConstraint>();
private MixedRealityTransform initialWorldPose;
/// <summary>
/// Adds a constraint to the manual selection list.
/// Note that only unique components will be added to the list.
/// </summary>
/// <param name="constraint">Constraint to add to the managers manual constraint list.</param>
/// <returns>Returns true if insertion was successful. If the component was already in the list the insertion will fail.</returns>
public bool AddConstraintToManualSelection(TransformConstraint constraint)
{
var existingConstraint = selectedConstraints.Find(t => t == constraint);
if (existingConstraint == null)
{
ConstraintUtils.AddWithPriority(ref selectedConstraints, constraint, new ConstraintExecOrderComparer());
}
return existingConstraint == null;
}
/// <summary>
/// Removes the given component from the manually selected constraint list.
/// </summary>
/// <param name="constraint">Constraint to remove.</param>
public void RemoveConstraintFromManualSelection(TransformConstraint constraint)
{
selectedConstraints.Remove(constraint);
}
public void ApplyScaleConstraints(ref MixedRealityTransform transform, bool isOneHanded, bool isNear)
{
ApplyConstraintsForType(ref transform, isOneHanded, isNear, TransformFlags.Scale);
}
public void ApplyRotationConstraints(ref MixedRealityTransform transform, bool isOneHanded, bool isNear)
{
ApplyConstraintsForType(ref transform, isOneHanded, isNear, TransformFlags.Rotate);
}
public void ApplyTranslationConstraints(ref MixedRealityTransform transform, bool isOneHanded, bool isNear)
{
ApplyConstraintsForType(ref transform, isOneHanded, isNear, TransformFlags.Move);
}
public void Initialize(MixedRealityTransform worldPose)
{
initialWorldPose = worldPose;
foreach (var constraint in constraints)
{
constraint.Initialize(worldPose);
}
}
/// <summary>
/// Re-sort list of constraints. Triggered by constraints
/// when their execution order is modified at runtime.
/// </summary>
internal void RefreshPriorities()
{
constraints.Sort(new ConstraintExecOrderComparer());
}
/// <summary>
/// Registering of a constraint during runtime. This method gets called by the constraint
/// components to auto register in their OnEnable method.
/// </summary>
/// <param name="constraint">Constraint to add to the manager.</param>
internal void AutoRegisterConstraint(TransformConstraint constraint)
{
// add to auto component list
if (constraint.isActiveAndEnabled)
{
ConstraintUtils.AddWithPriority(ref constraints, constraint, new ConstraintExecOrderComparer());
constraint.Initialize(initialWorldPose);
}
}
/// <summary>
/// Unregister a constraint from the manager.
/// Removes the constraint from the manual list if auto mode is disabled.
/// </summary>
/// <param name="constraint">Constraint to remove from the manager.</param>
internal void AutoUnregisterConstraint(TransformConstraint constraint)
{
constraints.Remove(constraint);
}
protected void Awake()
{
var constraintComponents = gameObject.GetComponents<TransformConstraint>();
foreach (var constraint in constraintComponents)
{
if (constraint.isActiveAndEnabled)
{
ConstraintUtils.AddWithPriority(ref constraints, constraint, new ConstraintExecOrderComparer());
}
}
}
private void ApplyConstraintsForType(ref MixedRealityTransform transform, bool isOneHanded, bool isNear, TransformFlags transformType)
{
ManipulationHandFlags handMode = isOneHanded ? ManipulationHandFlags.OneHanded : ManipulationHandFlags.TwoHanded;
ManipulationProximityFlags proximityMode = isNear ? ManipulationProximityFlags.Near : ManipulationProximityFlags.Far;
foreach (var constraint in constraints)
{
// If on manual mode, filter executed constraints by which have been manually selected
if (!autoConstraintSelection && !selectedConstraints.Contains(constraint))
{
continue;
}
if (constraint.isActiveAndEnabled &&
constraint.ConstraintType == transformType &&
constraint.HandType.IsMaskSet(handMode) &&
constraint.ProximityType.IsMaskSet(proximityMode))
{
constraint.ApplyConstraint(ref transform);
}
}
}
}
}