diff --git a/XRTK.SDK/Packages/com.xrtk.sdk/Features/Input/Handlers/ManipulationHandler.cs b/XRTK.SDK/Packages/com.xrtk.sdk/Features/Input/Handlers/ManipulationHandler.cs index a7f52753..1f6be574 100644 --- a/XRTK.SDK/Packages/com.xrtk.sdk/Features/Input/Handlers/ManipulationHandler.cs +++ b/XRTK.SDK/Packages/com.xrtk.sdk/Features/Input/Handlers/ManipulationHandler.cs @@ -8,6 +8,7 @@ using XRTK.Extensions; using XRTK.Interfaces.InputSystem; using XRTK.Interfaces.InputSystem.Handlers; +using XRTK.SDK.UX; using XRTK.Services; namespace XRTK.SDK.Input.Handlers @@ -23,6 +24,8 @@ public class ManipulationHandler : BaseInputHandler, IMixedRealityInputHandler, IMixedRealityInputHandler { + private const int IgnoreRaycastLayer = 2; + #region Input Actions [Header("Input Actions")] @@ -125,6 +128,19 @@ public MixedRealityInputAction CancelAction [Tooltip("The object to manipulate using this handler. Automatically uses this transform if none is set.")] private Transform manipulationTarget; + [SerializeField] + [Tooltip("The spatial mesh visibility while manipulating an object.")] + private SpatialMeshDisplayOptions spatialMeshVisibility = SpatialMeshDisplayOptions.Visible; + + /// + /// The spatial mesh visibility while manipulating an object. + /// + public SpatialMeshDisplayOptions SpatialMeshVisibility + { + get => spatialMeshVisibility; + set => spatialMeshVisibility = value; + } + [SerializeField] [Tooltip("Should the user press and hold the select action or press to hold and press again to release?")] private bool useHold = true; @@ -169,7 +185,7 @@ public float ScaleAmount [SerializeField] [Tooltip("The min and max size this object can be scaled to.")] - private Vector2 scaleConstraints = new Vector2(0.01f, 1f); + private Vector2 scaleConstraints = new Vector2(0.02f, 2f); /// /// The min and max size this object can be scaled to. @@ -267,9 +283,7 @@ public Vector2 NudgeConstraints /// /// Used to determine if the is currently being manipulated by the user. /// - public bool IsBeingHeld => isBeingHeld; - - private bool isBeingHeld = false; + public bool IsBeingHeld { get; private set; } = false; /// /// The updated extent of the pointer. @@ -294,62 +308,63 @@ public Vector2 NudgeConstraints /// /// Is the currently pressed? /// - public bool IsPressed => isPressed; - - private bool isPressed = false; + public bool IsPressed { get; private set; } = false; /// /// Is there currently a manipulation processing a rotation? /// - public bool IsRotating => isRotating; - - private bool isRotating = false; + public bool IsRotating { get; private set; } = false; /// /// Is scaling possible? /// - public bool IsScalingPossible => isScalePossible; - - private bool isScalePossible = false; + public bool IsScalingPossible { get; private set; } = false; /// /// Is nudge possible? /// - public bool IsNudgePossible => isNudgePossible; - - private bool isNudgePossible = false; + public bool IsNudgePossible { get; private set; } = false; /// /// Is rotation possible? /// - public bool IsRotationPossible => isRotationPossible; + public bool IsRotationPossible { get; private set; } = false; - private bool isRotationPossible = false; + private Vector3 prevScale = Vector3.one; private Vector3 prevPosition = Vector3.zero; + private Quaternion prevRotation = Quaternion.identity; - private Vector3 prevScale = Vector3.one; + private Vector3 grabbedPosition = Vector3.zero; - private Quaternion prevRotation = Quaternion.identity; + private BoundingBox boundingBox; + + private int prevPhysicsLayer; + private int boundingBoxPrevPhysicsLayer; #region Monobehaviour Implementation - private void Awake() + protected virtual void Awake() { if (manipulationTarget == null) { manipulationTarget = transform; } + + boundingBox = GetComponent(); } - private void Update() + protected virtual void Update() { - if (isBeingHeld) + if (IsBeingHeld) { - manipulationTarget.position = primaryPointer.Result.Details.Point; + if (!IsRotating && !IsScalingPossible) + { + manipulationTarget.position = grabbedPosition + primaryPointer.Result.Details.Point; + } } - if (isPressed && isNudgePossible && primaryPointer != null) + if (IsPressed && IsNudgePossible && primaryPointer != null) { primaryPointer.PointerExtent = updatedExtent; } @@ -359,7 +374,7 @@ protected override void OnDisable() { base.OnDisable(); - if (isBeingHeld) + if (IsBeingHeld) { EndHold(); } @@ -373,7 +388,7 @@ protected override void OnDisable() public virtual void OnInputDown(InputEventData eventData) { if (!eventData.used && - isBeingHeld && + IsBeingHeld && eventData.MixedRealityInputAction == cancelAction) { EndHold(true); @@ -385,7 +400,7 @@ public virtual void OnInputDown(InputEventData eventData) public virtual void OnInputUp(InputEventData eventData) { if (!eventData.used && - isBeingHeld && + IsBeingHeld && eventData.MixedRealityInputAction == cancelAction) { EndHold(true); @@ -396,7 +411,7 @@ public virtual void OnInputUp(InputEventData eventData) /// public virtual void OnInputChanged(InputEventData eventData) { - if (!isBeingHeld || + if (!IsBeingHeld || primaryInputSource == null || eventData.InputSource.SourceId != primaryInputSource.SourceId) { @@ -406,27 +421,29 @@ public virtual void OnInputChanged(InputEventData eventData) if (eventData.MixedRealityInputAction == touchpadPressAction && eventData.InputData <= 0.00001f) { - isRotating = false; + IsRotating = false; + IsNudgePossible = false; + IsScalingPossible = false; lastPositionReading.x = 0f; lastPositionReading.y = 0f; eventData.Use(); } - if (isRotating) { return; } + if (IsRotating) { return; } - if (!isPressed && + if (!IsPressed && eventData.MixedRealityInputAction == touchpadPressAction && eventData.InputData >= pressThreshold) { - isPressed = true; + IsPressed = true; eventData.Use(); } - if (isPressed && + if (IsPressed && eventData.MixedRealityInputAction == touchpadPressAction && eventData.InputData <= pressThreshold) { - isPressed = false; + IsPressed = false; eventData.Use(); } } @@ -434,13 +451,19 @@ public virtual void OnInputChanged(InputEventData eventData) /// public virtual void OnInputChanged(InputEventData eventData) { - if (!isBeingHeld || + // reset this in case we are rotating only. + IsScalingPossible = false; + + if (!IsBeingHeld || primaryInputSource == null || + primaryPointer == null || eventData.InputSource.SourceId != primaryInputSource.SourceId) { return; } + var pointerPosition = primaryPointer.Result.Details.Point; + // Filter our actions if (eventData.MixedRealityInputAction != nudgeAction || eventData.MixedRealityInputAction != scaleAction || @@ -453,66 +476,73 @@ public virtual void OnInputChanged(InputEventData eventData) absoluteInputData.x = Mathf.Abs(absoluteInputData.x); absoluteInputData.y = Mathf.Abs(absoluteInputData.y); - isRotationPossible = eventData.MixedRealityInputAction == rotateAction && - (absoluteInputData.x >= rotationZone.x || - absoluteInputData.y >= rotationZone.x); + IsRotationPossible = eventData.MixedRealityInputAction == rotateAction && + (absoluteInputData.x >= rotationZone.x || + absoluteInputData.y >= rotationZone.x); - if (!isPressed && - isRotationPossible && + if (!IsPressed && + IsRotationPossible && !lastPositionReading.x.Equals(0f) && !lastPositionReading.y.Equals(0f)) { var rotationAngle = Vector2.SignedAngle(lastPositionReading, eventData.InputData); if (Mathf.Abs(rotationAngle) > rotationAngleActivation) { - isRotating = true; + IsRotating = true; } - if (isRotating) + if (IsRotating) { - manipulationTarget.Rotate(0f, -rotationAngle, 0f, Space.Self); + manipulationTarget.position = grabbedPosition + pointerPosition; + manipulationTarget.RotateAround(pointerPosition, Vector3.up, -rotationAngle); + + if (prevPosition != Vector3.zero) + { + grabbedPosition = manipulationTarget.position - pointerPosition; + } + eventData.Use(); } } lastPositionReading = eventData.InputData; - if (!isPressed || isRotating) { return; } + if (!IsPressed || IsRotating) { return; } - isScalePossible = eventData.MixedRealityInputAction == scaleAction && absoluteInputData.x > 0f; - isNudgePossible = eventData.MixedRealityInputAction == nudgeAction && absoluteInputData.y > 0f && primaryPointer != null; + IsScalingPossible = eventData.MixedRealityInputAction == scaleAction && absoluteInputData.x > 0f; + IsNudgePossible = eventData.MixedRealityInputAction == nudgeAction && absoluteInputData.y > 0f; // Check to make sure that input values fall between min/max zone values - if (isScalePossible && + if (IsScalingPossible && (absoluteInputData.x <= scaleZone.x || absoluteInputData.x >= scaleZone.y)) { - isScalePossible = false; + IsScalingPossible = false; } // Check to make sure that input values fall between min/max zone values - if (isNudgePossible && + if (IsNudgePossible && (absoluteInputData.y <= nudgeZone.x || absoluteInputData.y >= nudgeZone.y)) { - isNudgePossible = false; + IsNudgePossible = false; } // Disable any actions if min zone values overlap. if (absoluteInputData.x <= scaleZone.x && absoluteInputData.y <= nudgeZone.x) { - isNudgePossible = false; - isScalePossible = false; + IsNudgePossible = false; + IsScalingPossible = false; } - if (isScalePossible && isNudgePossible) + if (IsScalingPossible && IsNudgePossible) { - isNudgePossible = false; - isScalePossible = false; + IsNudgePossible = false; + IsScalingPossible = false; } - if (isNudgePossible) + if (IsNudgePossible) { Debug.Assert(primaryPointer != null); var newExtent = primaryPointer.PointerExtent; @@ -548,7 +578,7 @@ public virtual void OnInputChanged(InputEventData eventData) eventData.Use(); } - if (isScalePossible) + if (IsScalingPossible) { var newScale = manipulationTarget.localScale; var currentScale = newScale; @@ -574,15 +604,24 @@ public virtual void OnInputChanged(InputEventData eventData) } } - manipulationTarget.localScale = newScale; + manipulationTarget.position = grabbedPosition + pointerPosition; + manipulationTarget.ScaleAround(pointerPosition, newScale); + + if (prevPosition != Vector3.zero) + { + grabbedPosition = manipulationTarget.position - pointerPosition; + } + eventData.Use(); } } - /// Calculates the extent of the nudge. + /// + /// Calculates the extent of the nudge. + /// /// The event data. /// The previous extent distance of the pointer and raycast. - /// + /// The new pointer extent. protected virtual float CalculateNudgeDistance(InputEventData eventData, float prevExtent) { return prevExtent + nudgeAmount * (eventData.InputData.y < 0f ? -1 : 1); @@ -617,7 +656,7 @@ public virtual void OnPointerUp(MixedRealityPointerEventData eventData) if (useHold) { - if (isBeingHeld) + if (IsBeingHeld) { EndHold(); } @@ -627,7 +666,7 @@ public virtual void OnPointerUp(MixedRealityPointerEventData eventData) } } - if (!useHold && isBeingHeld) + if (!useHold && IsBeingHeld) { EndHold(); } @@ -648,16 +687,9 @@ public virtual void OnPointerClicked(MixedRealityPointerEventData eventData) /// public virtual void BeginHold(MixedRealityPointerEventData eventData) { - if (isBeingHeld) { return; } - - isBeingHeld = true; + if (IsBeingHeld) { return; } - MixedRealityToolkit.InputSystem.PushModalInputHandler(gameObject); - MixedRealityToolkit.SpatialAwarenessSystem.SetMeshVisibility(SpatialMeshDisplayOptions.Collision); - - prevPosition = manipulationTarget.position; - prevScale = manipulationTarget.localScale; - prevRotation = manipulationTarget.rotation; + IsBeingHeld = true; if (primaryInputSource == null) { @@ -669,7 +701,33 @@ public virtual void BeginHold(MixedRealityPointerEventData eventData) primaryPointer = eventData.Pointer; } - manipulationTarget.SetCollidersActive(false); + MixedRealityToolkit.InputSystem.PushModalInputHandler(gameObject); + MixedRealityToolkit.SpatialAwarenessSystem.SetMeshVisibility(spatialMeshVisibility); + + var pointerPosition = primaryPointer.Result.Details.Point; + + prevPosition = manipulationTarget.position; + + if (prevPosition != Vector3.zero) + { + grabbedPosition = prevPosition - pointerPosition; + + // update the pointer extent to prevent the object from popping to the end of the pointer + var currentRaycastDistance = primaryPointer.Result.Details.RayDistance; + primaryPointer.PointerExtent = currentRaycastDistance; + } + + prevScale = manipulationTarget.localScale; + prevRotation = manipulationTarget.rotation; + + prevPhysicsLayer = manipulationTarget.gameObject.layer; + manipulationTarget.SetLayerRecursively(IgnoreRaycastLayer); + + if (boundingBox != null) + { + boundingBoxPrevPhysicsLayer = boundingBox.gameObject.layer; + boundingBox.transform.SetLayerRecursively(IgnoreRaycastLayer); + } eventData.Use(); } @@ -682,7 +740,7 @@ public virtual void BeginHold(MixedRealityPointerEventData eventData) /// public virtual void EndHold(bool isCanceled = false) { - if (!isBeingHeld) { return; } + if (!IsBeingHeld) { return; } MixedRealityToolkit.SpatialAwarenessSystem.SetMeshVisibility(SpatialMeshDisplayOptions.None); @@ -696,9 +754,15 @@ public virtual void EndHold(bool isCanceled = false) manipulationTarget.rotation = prevRotation; } - isBeingHeld = false; + IsBeingHeld = false; MixedRealityToolkit.InputSystem.PopModalInputHandler(); - manipulationTarget.SetCollidersActive(true); + + manipulationTarget.SetLayerRecursively(prevPhysicsLayer); + + if (boundingBox != null) + { + boundingBox.transform.SetLayerRecursively(boundingBoxPrevPhysicsLayer); + } } } } diff --git a/XRTK.SDK/Packages/com.xrtk.sdk/Features/UX/Scripts/BoundingBox/BoundingBox.cs b/XRTK.SDK/Packages/com.xrtk.sdk/Features/UX/Scripts/BoundingBox/BoundingBox.cs index 328fd05b..cd6bca2a 100644 --- a/XRTK.SDK/Packages/com.xrtk.sdk/Features/UX/Scripts/BoundingBox/BoundingBox.cs +++ b/XRTK.SDK/Packages/com.xrtk.sdk/Features/UX/Scripts/BoundingBox/BoundingBox.cs @@ -17,13 +17,22 @@ namespace XRTK.SDK.UX { + /// + /// The bounding box is a component for easily manipulating an object using a visual wireframe and handles. + /// + [RequireComponent(typeof(ManipulationHandler))] public class BoundingBox : MonoBehaviour { + /// + /// Rig component for handling input events. + /// private class BoundingBoxRig : BaseInputHandler, IMixedRealitySourceStateHandler, IMixedRealityPointerHandler, IMixedRealityInputHandler { + private const int IgnoreRaycastLayer = 2; + public override bool IsFocusRequired => false; public BoundingBox BoundingBoxParent @@ -42,6 +51,8 @@ public BoundingBox BoundingBoxParent private BoundingBox boundingBoxParent; + private int cachedTargetPrevLayer; + /// void IMixedRealityPointerHandler.OnPointerDown(MixedRealityPointerEventData eventData) { @@ -70,6 +81,11 @@ void IMixedRealityPointerHandler.OnPointerDown(MixedRealityPointerEventData even pointer.TryGetPointerPosition(out BoundingBoxParent.initialGrabPoint); BoundingBoxParent.ShowOneHandle(BoundingBoxParent.grabbedHandle); BoundingBoxParent.initialGazePoint = Vector3.zero; + cachedTargetPrevLayer = BoundingBoxParent.cachedTargetCollider.gameObject.layer; + BoundingBoxParent.cachedTargetCollider.transform.SetLayerRecursively(IgnoreRaycastLayer); + BoundingBoxParent.cachedTargetCollider.enabled = false; + BoundingBoxParent.transform.SetCollidersActive(false); + transform.SetCollidersActive(false); eventData.Use(); } } @@ -85,11 +101,17 @@ void IMixedRealityPointerHandler.OnPointerUp(MixedRealityPointerEventData eventD BoundingBoxParent.currentPointer = null; BoundingBoxParent.grabbedHandle = null; BoundingBoxParent.ResetHandleVisibility(); - eventData.Use(); MixedRealityToolkit.InputSystem.PopModalInputHandler(); + BoundingBoxParent.cachedTargetCollider.transform.SetLayerRecursively(cachedTargetPrevLayer); + BoundingBoxParent.transform.SetCollidersActive(true); + transform.SetCollidersActive(true); + eventData.Use(); } } + /// + void IMixedRealityPointerHandler.OnPointerClicked(MixedRealityPointerEventData eventData) { } + /// void IMixedRealityInputHandler.OnInputChanged(InputEventData eventData) { @@ -128,36 +150,10 @@ void IMixedRealitySourceStateHandler.OnSourceLost(SourceStateEventData eventData MixedRealityToolkit.InputSystem.PopModalInputHandler(); } } - - void IMixedRealityPointerHandler.OnPointerClicked(MixedRealityPointerEventData eventData) { } } #region Enums - /// - /// Enum which describes how an object's bounding box is to be flattened. - /// - private enum FlattenModeType - { - DoNotFlatten = 0, - /// - /// Flatten the X axis - /// - FlattenX, - /// - /// Flatten the Y axis - /// - FlattenY, - /// - /// Flatten the Z axis - /// - FlattenZ, - /// - /// Flatten the smallest relative axis if it falls below threshold - /// - FlattenAuto, - } - /// /// Enum which describes whether a bounding box handle which has been grabbed, is /// a Rotation Handle (sphere) or a Scale Handle( cube) @@ -171,7 +167,7 @@ private enum HandleType /// /// This enum describes which primitive type the wireframe portion of the bounding box - /// consists of. + /// consists of. /// /// /// Wireframe refers to the thin linkage between the handles. When the handles are invisible @@ -183,14 +179,6 @@ private enum WireframeType Cylindrical } - [Flags] - private enum CardinalAxisType - { - X = 1, - Y = 2, - Z = 3, - } - /// /// This enum defines how a particular controller rotates an object when a Rotate handle has been grabbed. /// @@ -212,7 +200,7 @@ private enum HandleMoveType [SerializeField] [Tooltip("Displays the rig in the hierarchy window. Useful for debugging the rig elements.")] - private bool showRig = false; + private bool showRigDebug = true; [Header("Bounds Calculation")] @@ -220,16 +208,29 @@ private enum HandleMoveType [Tooltip("For complex objects, automatic bounds calculation may not behave as expected. Use an existing Box Collider (even on a child object) to manually determine bounds of Bounding Box.")] private BoxCollider boxColliderToUse = null; - [Header("Behavior")] - - [SerializeField] - private bool activateOnStart = false; + /// + /// For complex objects, automatic bounds calculation may not behave as expected. + /// Use an existing Box Collider (even on a child object) to manually determine bounds of Bounding Box. + /// + public BoxCollider BoxColliderToUse + { + get => boxColliderToUse; + set + { + if (boxColliderToUse != value) + { + boxColliderToUse = value; + pendingRigReset = true; + } + } + } - [SerializeField] - private float scaleMaximum = 2.0f; + [Header("Behavior")] [SerializeField] - private float scaleMinimum = 0.2f; + [Tooltip("Should the bounding box be visible when this object is started?")] + [FormerlySerializedAs("activateOnStart")] + private bool activateOnEnable = false; [Header("Wireframe")] @@ -251,6 +252,7 @@ public bool DisplayHandles if (displayHandles != value) { displayHandles = value; + ResetHandleVisibility(); } } @@ -259,8 +261,37 @@ public bool DisplayHandles [SerializeField] private Vector3 wireframePadding = Vector3.zero; + /// + /// The distance to pad the wireframe around the bounds of the encapsulated object(s). + /// + public Vector3 WireFramePadding + { + get => wireframePadding; + set + { + wireframePadding = value; + pendingRigReset = true; + } + } + [SerializeField] - private FlattenModeType flattenAxis = FlattenModeType.DoNotFlatten; + private FlattenMode flattenAxis = FlattenMode.DoNotFlatten; + + /// + /// Describes how the bounding box should be flattened + /// + public FlattenMode FlattenAxis + { + get => flattenAxis; + set + { + if (flattenAxis != value) + { + flattenAxis = value; + Flatten(); + } + } + } [SerializeField] private WireframeType wireframeShape = WireframeType.Cubic; @@ -268,15 +299,69 @@ public bool DisplayHandles [SerializeField] private Material wireframeMaterial; + /// + /// The material to use for the wireframe. + /// + public Material WireframeMaterial + { + get => wireframeMaterial; + set + { + if (wireframeMaterial != value) + { + wireframeMaterial = value; + pendingRigReset = true; + } + } + } + [Header("Handles")] [SerializeField] [Tooltip("Default materials will be created for Handles and Wireframe if none is specified.")] private Material handleMaterial; + /// + /// The material for all the handles to use. + /// + /// + /// Default materials will be created for Handles and Wireframe if none is specified. + /// + public Material HandleMaterial + { + get => handleMaterial; + set + { + if (handleMaterial != value) + { + handleMaterial = value; + pendingRigReset = true; + } + } + } + [SerializeField] private Material handleGrabbedMaterial; + /// + /// The material to use when the handles are interacted with. + /// + /// + /// Default materials will be created for Handles and Wireframe if none is specified. + /// + public Material HandleGrabbedMaterial + { + get => handleGrabbedMaterial; + set + { + if (handleGrabbedMaterial != value) + { + handleGrabbedMaterial = value; + pendingRigReset = true; + } + } + } + [SerializeField] private bool showScaleHandles = true; @@ -292,6 +377,12 @@ public bool ShowScaleHandles if (showScaleHandles != value) { showScaleHandles = value; + + if (value) + { + displayHandles = true; + } + ResetHandleVisibility(); } } @@ -306,14 +397,19 @@ public bool ShowScaleHandles /// public bool ShowRotateHandles { - get => showRotateHandles; + get => ((showRotationHandlesPerAxis & (CardinalAxis)(-1)) != 0) && showRotateHandles; set { if (showRotateHandles != value) { showRotateHandles = value; - showRotationHandlesPerAxis = (CardinalAxisType)(!showRotateHandles ? 0 : -1); + if (value) + { + displayHandles = true; + } + + showRotationHandlesPerAxis = (CardinalAxis)(!showRotateHandles ? 0 : -1); ResetHandleVisibility(); } @@ -323,42 +419,124 @@ public bool ShowRotateHandles [EnumFlags] [SerializeField] [Tooltip("Only show rotation handles for these specific axes.")] - private CardinalAxisType showRotationHandlesPerAxis = (CardinalAxisType)(-1); + private CardinalAxis showRotationHandlesPerAxis = (CardinalAxis)(-1); + + /// + /// Show the rotation handles based on the bit mask. + /// + public CardinalAxis ShowRotationHandlesPerAxis + { + get => showRotationHandlesPerAxis; + set + { + if (showRotationHandlesPerAxis != value) + { + showRotationHandlesPerAxis = value; + ShowRotateHandles = true; + + ResetHandleVisibility(); + } + } + } [SerializeField] private float linkRadius = 0.005f; + /// + /// The thickness of the links connecting each of the bounding boxes handles. + /// + public float LinkRadius + { + get => linkRadius; + set + { + if (!linkRadius.Equals(value)) + { + linkRadius = value; + pendingRigReset = true; + } + } + } + [SerializeField] - private float ballRadius = 0.035f; + [FormerlySerializedAs("ballRadius")] + private float rotationHandleRadius = 0.035f; + + /// + /// The size of the rotation handles. + /// + public float RotationHandleRadius + { + get => rotationHandleRadius; + set + { + if (!rotationHandleRadius.Equals(value)) + { + rotationHandleRadius = value; + pendingRigReset = true; + } + } + } [SerializeField] - private float cornerRadius = 0.03f; + [FormerlySerializedAs("cornerRadius")] + private float scaleHandleRadius = 0.03f; + + /// + /// The size of the scale handles. + /// + public float ScaleHandleRadius + { + get => scaleHandleRadius; + set + { + if (!scaleHandleRadius.Equals(value)) + { + scaleHandleRadius = value; + pendingRigReset = true; + } + } + } #endregion Serialized Fields - private bool isActive = false; + /// + /// Used to reset the rig when properties have changed. + /// + [NonSerialized] + private bool pendingRigReset = false; + + [NonSerialized] + private bool isVisible = false; /// - /// This Public property sets whether the BoundingBox is active (visible) + /// Is the Bounding Box Rig visible? /// - public bool IsActive + public bool IsVisible { - get => isActive; + get => isVisible; set { - if (isActive != value) + if (isVisible != value) { - if (value) + if (rigRoot == null) { CreateRig(); - rigRoot.gameObject.SetActive(true); } else { - DestroyRig(); + ResetBoundingBoxBounds(); + } + + rigRoot.gameObject.SetActive(value); + cachedTargetCollider.enabled = value; + + if (value) + { + ResetHandleVisibility(); } - isActive = value; + isVisible = value; } } } @@ -366,20 +544,12 @@ public bool IsActive private IMixedRealityPointer currentPointer; private IMixedRealityInputSource currentInputSource; - private Vector3 initialGazePoint = Vector3.zero; + private ManipulationHandler manipulationHandler; + private Transform rigRoot; private BoxCollider cachedTargetCollider; - private Bounds cachedTargetColliderBounds; - private HandleMoveType handleMoveType = HandleMoveType.Point; - private List links; - private List corners; - private List balls; - private List cornerRenderers; - private List ballRenderers; - private List linkRenderers; - private List cornerColliders; - private List ballColliders; + private HandleMoveType handleMoveType = HandleMoveType.Point; private Ray currentGrabRay; private float initialGrabMag; @@ -389,6 +559,7 @@ public bool IsActive private Vector3 currentRotationAxis; private Vector3 currentBoundsExtents; private Vector3 initialGrabbedPosition; + private Vector3 initialGazePoint = Vector3.zero; private Vector3 currentPosePosition = Vector3.zero; private int[] flattenedHandles; @@ -401,39 +572,85 @@ public bool IsActive private Vector3[] boundsCorners = new Vector3[8]; - private readonly Vector3[] edgeCenters = new Vector3[12]; - private readonly CardinalAxisType[] edgeAxes = new CardinalAxisType[12]; - private static readonly int numCorners = 8; private static readonly int Color = Shader.PropertyToID("_Color"); private static readonly int InnerGlow = Shader.PropertyToID("_InnerGlow"); private static readonly int InnerGlowColor = Shader.PropertyToID("_InnerGlowColor"); + private readonly Vector3[] edgeCenters = new Vector3[12]; + private readonly CardinalAxis[] edgeAxes = new CardinalAxis[12]; + + private readonly List> links = new List>(8); + private readonly List> scaleHandles = new List>(8); + private readonly List> rotationHandles = new List>(8); + #region Monobehaviour Methods - private void Start() + private void OnEnable() { - if (activateOnStart) + manipulationHandler = gameObject.EnsureComponent(); + + if (activateOnEnable) { - IsActive = true; + IsVisible = true; + } + + if (IsVisible) + { + Debug.Assert(rigRoot != null); + + if (!rigRoot.gameObject.activeInHierarchy) + { + rigRoot.gameObject.SetActive(true); + ResetBoundingBoxBounds(); + ResetHandleVisibility(); + UpdateBounds(); + UpdateRigTransform(); + } } + + pendingRigReset = false; } private void Update() { - if (currentInputSource == null) + if (!isVisible) { return; } + + if (pendingRigReset) { - UpdateBounds(); + CreateRig(); + pendingRigReset = false; + return; } - else + + UpdateBounds(); + + if (currentInputSource != null) { - UpdateBounds(); TransformRig(); } UpdateRigTransform(); } + private void OnDisable() + { + if (rigRoot != null) + { + rigRoot.gameObject.SetActive(false); + } + + if (cachedTargetCollider != null) + { + cachedTargetCollider.enabled = false; + } + } + + private void OnDestroy() + { + DestroyRig(); + } + #endregion Monobehaviour Methods private void CreateRig() @@ -442,7 +659,7 @@ private void CreateRig() SetMaterials(); InstantiateRig(); - SetBoundingBoxCollider(); + ResetBoundingBoxBounds(); UpdateBounds(); CreateLinks(); @@ -451,53 +668,71 @@ private void CreateRig() UpdateRigTransform(); Flatten(); ResetHandleVisibility(); - rigRoot.gameObject.SetActive(false); } private void DestroyRig() { if (boxColliderToUse == null) { - Destroy(cachedTargetCollider); + if (cachedTargetCollider != null) + { + cachedTargetCollider.size -= wireframePadding; + Destroy(cachedTargetCollider); + } } else { boxColliderToUse.size -= wireframePadding; } - if (balls != null) + if (links != null) { - for (var i = 0; i < balls.Count; i++) + for (int i = 0; i < links.Count; i++) { - Destroy(balls[i]); + var linkTransform = links[i].Item1; + + if (linkTransform != null) + { + Destroy(linkTransform.gameObject); + } } - balls.Clear(); + links.Clear(); } - if (links != null) + if (scaleHandles != null) { - for (int i = 0; i < links.Count; i++) + for (var i = 0; i < scaleHandles.Count; i++) { - Destroy(links[i]); + var scaleHandle = scaleHandles[i].Item1; + + if (scaleHandle != null) + { + Destroy(scaleHandle.gameObject); + } } - links.Clear(); + scaleHandles.Clear(); } - if (corners != null) + if (rotationHandles != null) { - for (var i = 0; i < corners.Count; i++) + for (var i = 0; i < rotationHandles.Count; i++) { - Destroy(corners[i]); + var rotationHandle = rotationHandles[i].Item1; + + if (rotationHandle != null) + { + Destroy(rotationHandle.gameObject); + } } - corners.Clear(); + rotationHandles.Clear(); } if (rigRoot != null) { - Destroy(rigRoot); + Destroy(rigRoot.gameObject); } } @@ -598,23 +833,23 @@ private void ScaleByHandle(Vector3 newHandlePosition) var ratio = newMag / startMag; var newScale = ClampScale(initialScale * ratio, out _); - //scale from object center - transform.localScale = newScale; + // scale from object center + transform.ScaleAround(rigCentroid, newScale); } private Vector3 GetRotationAxis(GameObject handle) { - for (int i = 0; i < balls.Count; ++i) + for (int i = 0; i < rotationHandles.Count; ++i) { - if (handle == balls[i].gameObject) + if (handle == rotationHandles[i].Item1.gameObject) { switch (edgeAxes[i]) { - case CardinalAxisType.X: + case CardinalAxis.X: return rigRoot.transform.up; - case CardinalAxisType.Y: + case CardinalAxis.Y: return rigRoot.transform.forward; - case CardinalAxisType.Z: + case CardinalAxis.Z: return rigRoot.transform.right; } } @@ -625,20 +860,20 @@ private Vector3 GetRotationAxis(GameObject handle) private void CreateLinks() { - edgeAxes[0] = CardinalAxisType.Z; - edgeAxes[2] = CardinalAxisType.Z; - edgeAxes[4] = CardinalAxisType.Z; - edgeAxes[6] = CardinalAxisType.Z; + edgeAxes[0] = CardinalAxis.Z; + edgeAxes[2] = CardinalAxis.Z; + edgeAxes[4] = CardinalAxis.Z; + edgeAxes[6] = CardinalAxis.Z; - edgeAxes[1] = CardinalAxisType.X; - edgeAxes[3] = CardinalAxisType.X; - edgeAxes[5] = CardinalAxisType.X; - edgeAxes[7] = CardinalAxisType.X; + edgeAxes[1] = CardinalAxis.X; + edgeAxes[3] = CardinalAxis.X; + edgeAxes[5] = CardinalAxis.X; + edgeAxes[7] = CardinalAxis.X; - edgeAxes[08] = CardinalAxisType.Y; - edgeAxes[09] = CardinalAxisType.Y; - edgeAxes[10] = CardinalAxisType.Y; - edgeAxes[11] = CardinalAxisType.Y; + edgeAxes[08] = CardinalAxis.Y; + edgeAxes[09] = CardinalAxis.Y; + edgeAxes[10] = CardinalAxis.Y; + edgeAxes[11] = CardinalAxis.Y; for (int i = 0; i < edgeCenters.Length; ++i) { @@ -652,15 +887,15 @@ private void CreateLinks() switch (edgeAxes[i]) { - case CardinalAxisType.X: + case CardinalAxis.X: link.transform.localScale = new Vector3(linkRadius, linkDimensions.y, linkRadius); link.transform.Rotate(new Vector3(0.0f, 90.0f, 0.0f)); break; - case CardinalAxisType.Y: + case CardinalAxis.Y: link.transform.localScale = new Vector3(linkRadius, linkDimensions.z, linkRadius); link.transform.Rotate(new Vector3(90.0f, 0.0f, 0.0f)); break; - case CardinalAxisType.Z: + case CardinalAxis.Z: link.transform.localScale = new Vector3(linkRadius, linkDimensions.x, linkRadius); link.transform.Rotate(new Vector3(0.0f, 0.0f, 90.0f)); break; @@ -672,76 +907,74 @@ private void CreateLinks() var linkRenderer = link.GetComponent(); linkRenderer.shadowCastingMode = ShadowCastingMode.Off; linkRenderer.receiveShadows = false; - linkRenderers.Add(linkRenderer); + + var linkCollider = link.GetComponent(); + Destroy(linkCollider); if (wireframeMaterial != null) { linkRenderer.material = wireframeMaterial; } - links.Add(link.transform); + links.Add(new Tuple(link.transform, linkRenderer)); } } private void CreateScaleHandles() { - var scale = new Vector3(cornerRadius, cornerRadius, cornerRadius); + var scale = new Vector3(scaleHandleRadius, scaleHandleRadius, scaleHandleRadius); for (int i = 0; i < boundsCorners.Length; ++i) { - var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); - cube.name = $"ScaleHandle_{i}"; - cube.transform.localScale = scale; - cube.transform.position = boundsCorners[i]; - cube.transform.parent = rigRoot.transform; + var scaleHandle = GameObject.CreatePrimitive(PrimitiveType.Cube); + scaleHandle.name = $"ScaleHandle_{i}"; + scaleHandle.transform.localScale = scale; + scaleHandle.transform.position = boundsCorners[i]; + scaleHandle.transform.parent = rigRoot.transform; - var cubeRenderer = cube.GetComponent(); - cubeRenderer.shadowCastingMode = ShadowCastingMode.Off; - cubeRenderer.receiveShadows = false; - cornerRenderers.Add(cubeRenderer); - cornerColliders.Add(cube.GetComponent()); - corners.Add(cube.transform); + var scaleHandleRenderer = scaleHandle.GetComponent(); + scaleHandleRenderer.shadowCastingMode = ShadowCastingMode.Off; + scaleHandleRenderer.receiveShadows = false; if (handleMaterial != null) { - cubeRenderer.material = handleMaterial; + scaleHandleRenderer.material = handleMaterial; } + + scaleHandles.Add(new Tuple(scaleHandle.transform, scaleHandleRenderer, scaleHandle.GetComponent())); } } private void CreateRotationHandles() { - var radius = new Vector3(ballRadius, ballRadius, ballRadius); + var radius = new Vector3(rotationHandleRadius, rotationHandleRadius, rotationHandleRadius); for (int i = 0; i < edgeCenters.Length; ++i) { - var ball = GameObject.CreatePrimitive(PrimitiveType.Sphere); - ball.name = $"RotationHandle_{edgeAxes[i]}_{i}"; - ball.transform.localScale = radius; - ball.transform.position = edgeCenters[i]; - ball.transform.parent = rigRoot.transform; + var rotationHandle = GameObject.CreatePrimitive(PrimitiveType.Sphere); + rotationHandle.name = $"RotationHandle_{edgeAxes[i]}_{i}"; + rotationHandle.transform.localScale = radius; + rotationHandle.transform.position = edgeCenters[i]; + rotationHandle.transform.parent = rigRoot.transform; - var ballRenderer = ball.GetComponent(); - ballRenderer.shadowCastingMode = ShadowCastingMode.Off; - ballRenderer.receiveShadows = false; - ballRenderers.Add(ballRenderer); - ballColliders.Add(ball.GetComponent()); - balls.Add(ball.transform); + var rotationHandleRenderer = rotationHandle.GetComponent(); + rotationHandleRenderer.shadowCastingMode = ShadowCastingMode.Off; + rotationHandleRenderer.receiveShadows = false; if (handleMaterial != null) { - ballRenderer.material = handleMaterial; + rotationHandleRenderer.material = handleMaterial; } - if (showRotationHandlesPerAxis == 0 || - showRotationHandlesPerAxis != (CardinalAxisType)(-1) && (showRotationHandlesPerAxis ^ edgeAxes[i]) != 0) - { - ball.SetActive(false); - } + rotationHandles.Add(new Tuple(rotationHandle.transform, rotationHandleRenderer, rotationHandle.GetComponent(), edgeAxes[i])); } } - private void SetBoundingBoxCollider() + /// + /// Resets the bounds of the bounding box based on the if provided, + /// otherwise calculate it based on the colliders on each child object. + /// + public void ResetBoundingBoxBounds() { // Collider.bounds is world space bounding volume. // Mesh.bounds is local space bounding volume @@ -751,17 +984,22 @@ private void SetBoundingBoxCollider() { cachedTargetCollider = boxColliderToUse; cachedTargetCollider.transform.hasChanged = true; + cachedTargetCollider.size += wireframePadding; } else { + if (cachedTargetCollider != null) + { + cachedTargetCollider.size -= wireframePadding; + Destroy(cachedTargetCollider); + } var bounds = transform.GetColliderBounds(); - cachedTargetCollider = gameObject.EnsureComponent(); - cachedTargetCollider.center = bounds.center - transform.position; - cachedTargetCollider.size = bounds.size; - } - cachedTargetCollider.size += wireframePadding; + cachedTargetCollider = gameObject.AddComponent(); + cachedTargetCollider.center = transform.InverseTransformPoint(bounds.center); + cachedTargetCollider.size = (bounds.size / transform.localScale.x) + wireframePadding; + } } private void SetMaterials() @@ -809,39 +1047,30 @@ private void InstantiateRig() var rig = rigRootObject.AddComponent(); rig.BoundingBoxParent = this; - if (!showRig) + if (!showRigDebug) { rigRootObject.hideFlags = hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector; } rigRoot = rigRootObject.transform; - - corners = new List(); - cornerColliders = new List(); - cornerRenderers = new List(); - balls = new List(); - ballRenderers = new List(); - ballColliders = new List(); - links = new List(); - linkRenderers = new List(); } private void CalculateEdgeCenters() { if (boundsCorners != null && edgeCenters != null) { - edgeCenters[0] = (boundsCorners[0] + boundsCorners[1]) * 0.5f; - edgeCenters[1] = (boundsCorners[0] + boundsCorners[2]) * 0.5f; - edgeCenters[2] = (boundsCorners[3] + boundsCorners[2]) * 0.5f; - edgeCenters[3] = (boundsCorners[3] + boundsCorners[1]) * 0.5f; - - edgeCenters[4] = (boundsCorners[4] + boundsCorners[5]) * 0.5f; - edgeCenters[5] = (boundsCorners[4] + boundsCorners[6]) * 0.5f; - edgeCenters[6] = (boundsCorners[7] + boundsCorners[6]) * 0.5f; - edgeCenters[7] = (boundsCorners[7] + boundsCorners[5]) * 0.5f; - - edgeCenters[8] = (boundsCorners[0] + boundsCorners[4]) * 0.5f; - edgeCenters[9] = (boundsCorners[1] + boundsCorners[5]) * 0.5f; + edgeCenters[00] = (boundsCorners[0] + boundsCorners[1]) * 0.5f; + edgeCenters[01] = (boundsCorners[0] + boundsCorners[2]) * 0.5f; + edgeCenters[02] = (boundsCorners[3] + boundsCorners[2]) * 0.5f; + edgeCenters[03] = (boundsCorners[3] + boundsCorners[1]) * 0.5f; + + edgeCenters[04] = (boundsCorners[4] + boundsCorners[5]) * 0.5f; + edgeCenters[05] = (boundsCorners[4] + boundsCorners[6]) * 0.5f; + edgeCenters[06] = (boundsCorners[7] + boundsCorners[6]) * 0.5f; + edgeCenters[07] = (boundsCorners[7] + boundsCorners[5]) * 0.5f; + + edgeCenters[08] = (boundsCorners[0] + boundsCorners[4]) * 0.5f; + edgeCenters[09] = (boundsCorners[1] + boundsCorners[5]) * 0.5f; edgeCenters[10] = (boundsCorners[2] + boundsCorners[6]) * 0.5f; edgeCenters[11] = (boundsCorners[3] + boundsCorners[7]) * 0.5f; } @@ -850,7 +1079,7 @@ private void CalculateEdgeCenters() private Vector3 ClampScale(Vector3 scale, out bool clamped) { var finalScale = scale; - var maximumScale = initialScale * scaleMaximum; + var maximumScale = initialScale * manipulationHandler.ScaleConstraints.y; clamped = false; if (scale.x > maximumScale.x || scale.y > maximumScale.y || scale.z > maximumScale.z) @@ -859,7 +1088,7 @@ private Vector3 ClampScale(Vector3 scale, out bool clamped) clamped = true; } - var minimumScale = initialScale * scaleMinimum; + var minimumScale = initialScale * manipulationHandler.ScaleConstraints.x; if (finalScale.x < minimumScale.x || finalScale.y < minimumScale.y || finalScale.z < minimumScale.z) { @@ -880,29 +1109,34 @@ private Vector3 GetLinkDimensions() private void ResetHandleVisibility() { - bool isVisible; + bool isHandlesVisible; - //set balls visibility - if (balls != null) + if (rotationHandles != null) { - isVisible = (!displayHandles && showRotateHandles); + isHandlesVisible = displayHandles && ShowRotateHandles; - for (int i = 0; i < ballRenderers.Count; ++i) + for (int i = 0; i < rotationHandles.Count; ++i) { - ballRenderers[i].material = handleMaterial; - ballRenderers[i].enabled = isVisible; + (Transform handleTransform, Renderer handleRenderer, Collider _, CardinalAxis axis) = rotationHandles[i]; + + var IsDisabled = (showRotationHandlesPerAxis == 0 || + showRotationHandlesPerAxis != (CardinalAxis)(-1) && + (showRotationHandlesPerAxis & axis) == 0); + + handleRenderer.material = handleMaterial; + handleTransform.gameObject.SetRenderingActive(isHandlesVisible && !IsDisabled); } } - //set corner visibility - if (corners != null) + if (scaleHandles != null) { - isVisible = (!displayHandles && showScaleHandles); + isHandlesVisible = displayHandles && showScaleHandles; - for (int i = 0; i < cornerRenderers.Count; ++i) + for (int i = 0; i < scaleHandles.Count; ++i) { - cornerRenderers[i].material = handleMaterial; - cornerRenderers[i].enabled = isVisible; + var scaleHandleRenderer = scaleHandles[i].Item2; + scaleHandleRenderer.material = handleMaterial; + scaleHandleRenderer.gameObject.SetRenderingActive(isHandlesVisible); } } @@ -911,21 +1145,21 @@ private void ResetHandleVisibility() private void ShowOneHandle(GameObject handle) { - //turn off all balls - if (balls != null) + //turn off all rotation handles + if (rotationHandles != null) { - for (int i = 0; i < ballRenderers.Count; ++i) + for (int i = 0; i < rotationHandles.Count; ++i) { - ballRenderers[i].enabled = false; + rotationHandles[i].Item2.enabled = false; } } - //turn off all corners - if (corners != null) + //turn off all scale handles + if (scaleHandles != null) { - for (int i = 0; i < cornerRenderers.Count; ++i) + for (int i = 0; i < scaleHandles.Count; ++i) { - cornerRenderers[i].enabled = false; + scaleHandles[i].Item2.enabled = false; } } @@ -940,7 +1174,7 @@ private void ShowOneHandle(GameObject handle) private void UpdateBounds() { - if (cachedTargetCollider == null) { return; } + Debug.Assert(cachedTargetCollider != null); // Store current rotation then zero out the rotation so that the bounds // are computed when the object is in its 'axis aligned orientation'. @@ -955,19 +1189,19 @@ private void UpdateBounds() if (boundsExtents != Vector3.zero) { - if (flattenAxis == FlattenModeType.FlattenAuto) + if (flattenAxis == FlattenMode.FlattenAuto) { var min = Mathf.Min(boundsExtents.x, Mathf.Min(boundsExtents.y, boundsExtents.z)); flattenAxis = min.Equals(boundsExtents.x) - ? FlattenModeType.FlattenX + ? FlattenMode.FlattenX : (min.Equals(boundsExtents.y) - ? FlattenModeType.FlattenY - : FlattenModeType.FlattenZ); + ? FlattenMode.FlattenY + : FlattenMode.FlattenZ); } - boundsExtents.x = flattenAxis == FlattenModeType.FlattenX ? 0.0f : boundsExtents.x; - boundsExtents.y = flattenAxis == FlattenModeType.FlattenY ? 0.0f : boundsExtents.y; - boundsExtents.z = flattenAxis == FlattenModeType.FlattenZ ? 0.0f : boundsExtents.z; + boundsExtents.x = flattenAxis == FlattenMode.FlattenX ? 0.0f : boundsExtents.x; + boundsExtents.y = flattenAxis == FlattenMode.FlattenY ? 0.0f : boundsExtents.y; + boundsExtents.z = flattenAxis == FlattenMode.FlattenZ ? 0.0f : boundsExtents.z; currentBoundsExtents = boundsExtents; GetCornerPositionsFromBounds(new Bounds(Vector3.zero, boundsExtents * 2.0f), ref boundsCorners); @@ -977,33 +1211,34 @@ private void UpdateBounds() private void UpdateRigTransform() { - if (rigRoot == null) { return; } + Debug.Assert(rigRoot != null); + Debug.Assert(cachedTargetCollider != null); rigRoot.rotation = Quaternion.identity; rigRoot.position = Vector3.zero; - for (int i = 0; i < corners.Count; ++i) + for (int i = 0; i < scaleHandles.Count; ++i) { - corners[i].position = boundsCorners[i]; + scaleHandles[i].Item1.position = boundsCorners[i]; } var linkDimensions = GetLinkDimensions(); for (int i = 0; i < edgeCenters.Length; ++i) { - balls[i].position = edgeCenters[i]; - links[i].position = edgeCenters[i]; + rotationHandles[i].Item1.position = edgeCenters[i]; + links[i].Item1.position = edgeCenters[i]; switch (edgeAxes[i]) { - case CardinalAxisType.X: - links[i].localScale = new Vector3(linkRadius, linkDimensions.y, linkRadius); + case CardinalAxis.X: + links[i].Item1.localScale = new Vector3(linkRadius, linkDimensions.y, linkRadius); break; - case CardinalAxisType.Y: - links[i].localScale = new Vector3(linkRadius, linkDimensions.z, linkRadius); + case CardinalAxis.Y: + links[i].Item1.localScale = new Vector3(linkRadius, linkDimensions.z, linkRadius); break; - case CardinalAxisType.Z: - links[i].localScale = new Vector3(linkRadius, linkDimensions.x, linkRadius); + case CardinalAxis.Z: + links[i].Item1.localScale = new Vector3(linkRadius, linkDimensions.x, linkRadius); break; } } @@ -1015,17 +1250,17 @@ private void UpdateRigTransform() private HandleType GetHandleType(GameObject handle) { - for (int i = 0; i < balls.Count; ++i) + for (int i = 0; i < rotationHandles.Count; ++i) { - if (handle == balls[i].gameObject) + if (handle == rotationHandles[i].Item1.gameObject) { return HandleType.Rotation; } } - for (int i = 0; i < corners.Count; ++i) + for (int i = 0; i < scaleHandles.Count; ++i) { - if (handle == corners[i].gameObject) + if (handle == scaleHandles[i].Item1.gameObject) { return HandleType.Scale; } @@ -1040,25 +1275,29 @@ private Collider GetGrabbedCollider(Ray ray, out float distance) float closestDistance = float.MaxValue; Collider closestCollider = null; - for (int i = 0; i < cornerColliders.Count; ++i) + for (int i = 0; i < scaleHandles.Count; ++i) { - if (cornerRenderers[i].enabled && - cornerColliders[i].bounds.IntersectRay(ray, out currentDistance) && + (Transform _, Renderer handleRenderer, Collider handleCollider) = scaleHandles[i]; + + if (handleRenderer.enabled && + handleCollider.bounds.IntersectRay(ray, out currentDistance) && currentDistance < closestDistance) { closestDistance = currentDistance; - closestCollider = cornerColliders[i]; + closestCollider = handleCollider; } } - for (int i = 0; i < ballColliders.Count; ++i) + for (int i = 0; i < rotationHandles.Count; ++i) { - if (ballRenderers[i].enabled && - ballColliders[i].bounds.IntersectRay(ray, out currentDistance) && + (Transform _, Renderer handleRenderer, Collider handleCollider, CardinalAxis _) = rotationHandles[i]; + + if (handleRenderer.enabled && + handleCollider.bounds.IntersectRay(ray, out currentDistance) && currentDistance < closestDistance) { closestDistance = currentDistance; - closestCollider = ballColliders[i]; + closestCollider = handleCollider; } } @@ -1082,13 +1321,13 @@ private void Flatten() { switch (flattenAxis) { - case FlattenModeType.FlattenX: + case FlattenMode.FlattenX: flattenedHandles = new[] { 0, 4, 2, 6 }; break; - case FlattenModeType.FlattenY: + case FlattenMode.FlattenY: flattenedHandles = new[] { 1, 3, 5, 7 }; break; - case FlattenModeType.FlattenZ: + case FlattenMode.FlattenZ: flattenedHandles = new[] { 9, 10, 8, 11 }; break; } @@ -1097,7 +1336,7 @@ private void Flatten() { for (int i = 0; i < flattenedHandles.Length; ++i) { - linkRenderers[flattenedHandles[i]].enabled = false; + links[flattenedHandles[i]].Item2.gameObject.SetRenderingActive(false); } } } @@ -1108,7 +1347,7 @@ private void SetHiddenHandles() { for (int i = 0; i < flattenedHandles.Length; ++i) { - ballRenderers[flattenedHandles[i]].enabled = false; + rotationHandles[flattenedHandles[i]].Item1.gameObject.SetRenderingActive(false); } } }