public void RpcAlignWithTargetObject(WhichTargetPoint whichTargetPoint)
    {
        ControllerParent controllerParent = whichTargetPoint == WhichTargetPoint.Left ? LeftControllerParent : RightControllerParent;

        if (CurrentControllerParent == controllerParent) // check to make sure we aren't using Left controller to align with Right-controller-spawned object, and vice versa
        {
            // callbacks
            CurrentControllerParent.TargetPoint.OnAlignmentConfirmed();
            CurrentSpawnedPoseParent.TargetPoint.OnAlignmentConfirmed();

            LeftControllerParent.TargetPoint.Stick.gameObject.SetActive(false);
            RightControllerParent.TargetPoint.Stick.gameObject.SetActive(false);

            // compute alignment
            float distanceMeters = Vector3.Distance(CurrentControllerParent.transform.position, CurrentSpawnedPoseParent.transform.position);
            float angleDegrees   = Quaternion.Angle(CurrentControllerParent.transform.rotation, CurrentSpawnedPoseParent.transform.rotation);

            Debug.Log("distance: " + distanceMeters + " meters, angle: " + angleDegrees + " degrees");

            DestroyCurrentSpawnedPose();

            CurrentControllerParent     = null;
            CurrentFreeModeControlState = FreeModeControlState.PlacingTargetObject;
        }
    }
    private void OnControllerClick(WhichTargetPoint whichTargetPoint)
    {
        if (ObjectManager.Instance.SessionManager.IsInFreeMode())
        {
            switch (CurrentFreeModeControlState)
            {
            case FreeModeControlState.PlacingTargetObject:

                Transform controllerParent = whichTargetPoint == WhichTargetPoint.Left ? ObjectManager.Instance.ViveLeftControllerParent : ObjectManager.Instance.ViveRightControllerParent;

                Vector3    positionInViveSpace = ViveOrigin.InverseTransformPoint(controllerParent.position);
                Quaternion rotationInViveSpace = Quaternion.Inverse(ViveOrigin.localRotation) * controllerParent.rotation;

                RpcPlaceTargetObjectAtCustomPose(whichTargetPoint, positionInViveSpace, rotationInViveSpace);

                break;

            case FreeModeControlState.AligningWithTargetObject:
                RpcAlignWithTargetObject(whichTargetPoint);
                break;

            default:
                Debug.LogError("Unhandled FreeModeControlState: " + CurrentFreeModeControlState);
                break;
            }
        }
        else
        {
            if (ObjectManager.Instance.SessionManager.CurrentState == SessionManager.State.Running)
            {
                ControllerParent controllerParent = whichTargetPoint == WhichTargetPoint.Left ? LeftControllerParent : RightControllerParent;

                if (CurrentControllerParent == controllerParent) // check to make sure we aren't using Left controller to align with Right-controller-spawned object, and vice versa
                {
                    // if we're not in free mode, then a click of the controller only means we are aligning with an already-provided pose

                    // check our suppression distance, to help avoid double-clicks on accident
                    if (CurrentControllerParent != null && CurrentSpawnedPoseParent != null)
                    {
                        bool passedSuppressionCheck = (!EnableDistanceBasedClickSuppression) || (Vector3.Distance(CurrentControllerParent.TargetPoint.transform.position, CurrentSpawnedPoseParent.TargetPoint.transform.position) < SuppressAccidentalClickDistanceMeters);

                        float currentTime = Time.time;

                        passedSuppressionCheck = passedSuppressionCheck && ((!EnableTimeBasedClickSuppression) || ((currentTime - _lastClickTime) > SuppressAccidentalClickTimeSeconds));

                        if (passedSuppressionCheck)
                        {
                            _lastClickTime = currentTime;

                            ObjectManager.Instance.SessionManager.DoAlignment(whichTargetPoint);
                        }
                    }
                }
            }
        }
    }
    public void DoClearAnySpawnedFreeModePose()
    {
        Debug.Log("start DoClearAnySpawnedFreeModePose");
        // callbacks
        if (CurrentControllerParent != null && CurrentControllerParent.TargetPoint != null)
        {
            CurrentControllerParent.TargetPoint.OnAlignmentConfirmed();
        }

        if (CurrentSpawnedPoseParent != null && CurrentSpawnedPoseParent.TargetPoint != null)
        {
            CurrentSpawnedPoseParent.TargetPoint.OnAlignmentConfirmed();
        }

        DestroyCurrentSpawnedPose();

        CurrentControllerParent = null;

        CurrentFreeModeControlState = FreeModeControlState.PlacingTargetObject;
    }
    public void RpcPlaceTargetObjectAtCustomPose(WhichTargetPoint whichTargetPoint, Vector3 positionInViveSpace, Quaternion rotationInViveSpace)
    {
        ControllerParent controllerParent = whichTargetPoint == WhichTargetPoint.Left ? LeftControllerParent : RightControllerParent;

        CurrentSpawnedPoseParent = Instantiate(SpawnedPoseParentPrefab, ViveOrigin);
        CurrentSpawnedPoseParent.transform.localPosition = positionInViveSpace;
        CurrentSpawnedPoseParent.transform.localRotation = rotationInViveSpace;

        CurrentControllerParent = controllerParent;

        // callbacks
        CurrentControllerParent.TargetPoint.OnTargetSpawned(CurrentSpawnedPoseParent.TargetPoint, CurrentAlignmentInterfaceType, CurrentControllerTargetPointLocation);
        CurrentSpawnedPoseParent.TargetPoint.OnTargetSpawned(CurrentControllerParent.TargetPoint, CurrentAlignmentInterfaceType, CurrentControllerTargetPointLocation);

        CurrentFreeModeControlState = FreeModeControlState.AligningWithTargetObject;

        LeftControllerParent.TargetPoint.Stick.gameObject.SetActive(false);
        RightControllerParent.TargetPoint.Stick.gameObject.SetActive(false);

        if (!ObjectManager.Instance.SessionManager.IsInFreeMode())
        {
            ObjectManager.Instance.SessionManager.Log_OnPoseSpawned();
        }
    }