Esempio n. 1
0
        public void UpdateNode(XRNodeState node)
        {
            if (node.nodeType == XRNode.HardwareTracker)
            {
                var hwId = GetHardwareId(node);

                if (!_activeTrackers.ContainsKey(hwId))
                {
                    Debug.LogWarning($"No key {hwId}");
                    AddNode(node);
                    return;
                }

                var tracker = _activeTrackers[hwId];
                node.TryGetPosition(out var position);
                node.TryGetRotation(out var rotation);
                tracker.Rotation = rotation * RotationOffset;
                tracker.Position = position + tracker.Rotation * PositionOffset;

                tracker.LastUpdate = Time.unscaledTime;

                // if tracking is lost, tracker is put at (0, 0, 0)
                tracker.IsActive = (position - Vector3.zero).sqrMagnitude > Mathf.Epsilon;
            }
            else
            {
                var hwId = GetHardwareId(node);

                if (_activeTrackers.ContainsKey(hwId))
                {
                    Debug.LogWarning($"Tracker {hwId} is classified as {node.nodeType} but in activeTrackers");
                }
            }
        }
Esempio n. 2
0
    /// <summary>
    /// Initialize the ZED's tracking with the current HMD position and HMD-ZED calibration.
    /// This causes the ZED's internal tracking to start where the HMD is, despite being initialized later than the HMD.
    /// </summary>
    /// <returns>Initial offset for the ZED's tracking. </returns>
    public Pose InitTrackingAR()
    {
        if (manager == null)
        {
            return(new Pose());
        }

        Transform tmpHMD = transform;

#if UNITY_2019_1_OR_NEWER
        InputTracking.GetNodeStates(nodeStates);
        XRNodeState nodeState = nodeStates.Find(node => node.nodeType == XRNode.Head);
        nodeState.TryGetRotation(out Quaternion rot);
        nodeState.TryGetPosition(out Vector3 pos);
        Pose hmdTransform = new Pose(pos, rot);
#else
        tmpHMD.position = InputTracking.GetLocalPosition(XRNode.Head);
        tmpHMD.rotation = InputTracking.GetLocalRotation(XRNode.Head);
#endif

        Quaternion r            = Quaternion.identity;
        Vector3    t            = Vector3.zero;
        Pose       const_offset = new Pose(t, r);
        dllz_drift_corrector_set_calibration_const_offset_transform(ref const_offset);

        zedCamera.ResetTrackingWithOffset(tmpHMD.rotation, tmpHMD.position, HmdToZEDCalibration.rotation, HmdToZEDCalibration.translation);

        return(new Pose(tmpHMD.position, tmpHMD.rotation));
    }
Esempio n. 3
0
    private void CheckPlayerPos()
    {
        // circle of bubble:
        // compare against center of sphere

        InputTracking.GetNodeStates(nodeStatesCache);
        Vector3 hmd_pos = new Vector3();

        for (int i = 0; i < nodeStatesCache.Count; i++)
        {
            XRNodeState nodeState = nodeStatesCache[i];
            if (nodeState.nodeType == XRNode.CenterEye)
            {
                nodeState.TryGetPosition(out hmd_pos);
            }
        }

        if (Vector2.Distance(new Vector2(hmd_pos.x, hmd_pos.y), new Vector2(0, 0)) < bubble_visual.transform.localScale.x / 1.5)
        {
            // within - fine
        }
        else
        {
            state_handler.SetState(GameState.GAMEOVER);
        }
    }
        /// <inheritdoc />
        public override void UpdateController()
        {
            if (!Enabled)
            {
                return;
            }

            Profiler.BeginSample("[MRTK] GenericOpenVRController.UpdateController");

            InputTracking.GetNodeStates(nodeStates);

            for (int i = 0; i < nodeStates.Count; i++)
            {
                if (nodeStates[i].nodeType == nodeType)
                {
                    var xrNodeState = nodeStates[i];
                    UpdateControllerData(xrNodeState);
                    LastXrNodeStateReading = xrNodeState;
                    break;
                }
            }

            base.UpdateController();

            Profiler.EndSample(); // UpdateController
        }
Esempio n. 5
0
        protected override float GetAxisValue(XRNodeState node, InputAxis axis)
        {
            var controller = GetController(node);

            OVRPlugin.ControllerState4 state = OVRPlugin.GetControllerState4((uint)controller);

            switch (axis)
            {
            case InputAxis.MainTrigger:
                return(controller == OVRInput.Controller.LTouch ? state.LIndexTrigger : state.RIndexTrigger);

            case InputAxis.Grip:
                return(controller == OVRInput.Controller.LTouch ? state.LHandTrigger : state.RHandTrigger);

            case InputAxis.JoypadX:
            case InputAxis.JoypadY: {
                var joy = controller == OVRInput.Controller.LTouch ? state.LThumbstick : state.RThumbstick;
                return(axis == InputAxis.JoypadX ? joy.x : joy.y);
            }

            case InputAxis.Joypad: {
                var buttonId = controller == OVRInput.Controller.LTouch ? 0x00000400 : 0x00000004;                 //see enum ovrButton_ in OVER_CAPI.h
                return((state.Buttons & buttonId) != 0 ? 1 : 0);
            }

            default:
                return(0);
            }
        }
Esempio n. 6
0
        private Pose GetPoseInput(ulong action, XRNodeState node)
        {
            InputPoseActionData_t data = new InputPoseActionData_t();

            var res = OpenVR.Input.GetPoseActionData(
                action,
                XRDevice.GetTrackingSpaceType() == TrackingSpaceType.RoomScale ? ETrackingUniverseOrigin.TrackingUniverseStanding : ETrackingUniverseOrigin.TrackingUniverseSeated,
                0,
                ref data,
                (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(InputPoseActionData_t)),
                node.nodeType == XRNode.LeftHand ? leftHand : rightHand
                );

            if (res != EVRInputError.None)
            {
                throw new ApplicationException("Failed to get pose input data " + node.nodeType + ": " + res);
            }

            var matRaw = data.pose.mDeviceToAbsoluteTracking;
            var mat    = new Matrix4x4(
                new Vector4(matRaw.m0, matRaw.m1, matRaw.m2, 0),
                new Vector4(matRaw.m4, matRaw.m5, matRaw.m6, 0),
                new Vector4(matRaw.m8, matRaw.m9, matRaw.m10, 0),
                new Vector4(0, 0, 0, 1)
                );
            var rot = mat.rotation;

//		rot.x *= -1;
            rot.z *= -1;

            return(new Pose {
                pos = new Vector3(matRaw.m3, matRaw.m7, -matRaw.m11),
                rot = rot
            });
        }
Esempio n. 7
0
    /// <summary>
    /// Before the ZED is ready, lock the quads in front of the cameras as latency correction isn't available yet.
    /// This allows us to see the loading messages (and other virtual objects if desired) while the ZED is still loading.
    /// Called by Camera.OnPreRender anytime any camera renders.
    /// </summary>
    /// <param name="cam">Cam.</param>
    public void PreRender(Camera cam)
    {
        if (cam == finalLeftEye || cam == finalRightEye)
        {
            if ((!manager.IsZEDReady && manager.IsStereoRig))
            {
#if UNITY_2019_1_OR_NEWER
                InputTracking.GetNodeStates(nodeStates);
                XRNodeState nodeState = nodeStates.Find(node => node.nodeType == XRNode.Head);
                nodeState.TryGetRotation(out Quaternion rot);
                nodeState.TryGetPosition(out Vector3 pos);

                quadLeft.localRotation = rot;
                quadLeft.localPosition = pos + quadLeft.localRotation * offset;

                quadRight.localRotation = rot;
                quadRight.localPosition = pos + quadRight.localRotation * offset;
#else
                quadLeft.localRotation = InputTracking.GetLocalRotation(XRNode.Head);
                quadLeft.localPosition = InputTracking.GetLocalPosition(XRNode.Head) + quadLeft.localRotation * offset;

                quadRight.localRotation = InputTracking.GetLocalRotation(XRNode.Head);
                quadRight.localPosition = InputTracking.GetLocalPosition(XRNode.Head) + quadRight.localRotation * offset;
#endif
            }
        }
    }
Esempio n. 8
0
        /// <inheritdoc />
        public override void UpdateController()
        {
            using (UpdateControllerPerfMarker.Auto())
            {
                if (!Enabled)
                {
                    return;
                }

                InputTracking.GetNodeStates(nodeStates);

                for (int i = 0; i < nodeStates.Count; i++)
                {
                    if (nodeStates[i].nodeType == nodeType)
                    {
                        var xrNodeState = nodeStates[i];
                        UpdateControllerData(xrNodeState);
                        LastXrNodeStateReading = xrNodeState;
                        break;
                    }
                }

                base.UpdateController();
            }
        }
Esempio n. 9
0
        private static PosRot GetTrackerWorldPosRot(XRNodeState tracker)
        {
            Vector3    pos = new Vector3();
            Quaternion rot = new Quaternion();

            try
            {
                var notes = new List <XRNodeState>();
                InputTracking.GetNodeStates(notes);
                foreach (XRNodeState note in notes)
                {
                    if (note.uniqueID != tracker.uniqueID)
                    {
                        continue;
                    }
                    if (note.TryGetPosition(out pos) && note.TryGetRotation(out rot))
                    {
                        var roomCenter   = BeatSaberUtil.GetRoomCenter();
                        var roomRotation = BeatSaberUtil.GetRoomRotation();
                        pos  = roomRotation * pos;
                        pos += roomCenter;
                        rot  = roomRotation * rot;
                    }
                }
            }
            catch (Exception e)
            {
                Logger.Log(e.Message + "\n" + e.StackTrace, Logger.LogLevel.Error);
            }
            return(new PosRot(pos, rot));
        }
Esempio n. 10
0
        public override JoyPadType GetJoypadTypes(XRNodeState node)
        {
            if (jpType != JoyPadType.Unknown)
            {
                return(jpType);
            }

            if (mode == InputMode.Direct)
            {
                var name = GetNodeName(node);

                if (name.Contains("Oculus Touch Controller") || name.StartsWith("Oculus Rift CV1"))
                {
                    //OpenVR gives us "Oculus Rift CV1 (Left Controller)" etc. where I wish it would mention the type of controller (Touch)
                    return(jpType = JoyPadType.Joystick);
                }
                else if (name.StartsWith("Vive Controller"))
                {
                    return(jpType = JoyPadType.TouchPad);
                }
                else
                {
                    Debug.LogWarning("Unknown controller type: " + name);
                    return(jpType = JoyPadType.None);
                }
            }
            else
            {
                return(jpType = (JoyPadType.Joystick | JoyPadType.TouchPad));
            }
        }
Esempio n. 11
0
        protected override float GetAxisValue(XRNodeState node, InputAxis axis)
        {
            ReadState(node);

            switch (axis)
            {
            case InputAxis.TouchPad:
                return((lastState.ulButtonTouched & 1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad) != 0 ? 1 : 0);

            case InputAxis.MainTrigger:
                return(lastState.rAxis1.x);

            case InputAxis.Grip:
                return((lastState.ulButtonPressed & (1ul << (int)EVRButtonId.k_EButton_Grip)) != 0 ? 1 : 0);

            case InputAxis.JoypadX:
                return(lastState.rAxis0.x);

            case InputAxis.JoypadY:
                return(lastState.rAxis0.y);

            case InputAxis.Joypad:
                return((lastState.ulButtonPressed & (1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad)) != 0 ? 1 : 0);

            case InputAxis.Application:
                return((lastState.ulButtonPressed & (1ul << (int)EVRButtonId.k_EButton_ApplicationMenu)) != 0 ? 1 : 0);

            default:
                throw new ArgumentOutOfRangeException("axis", axis, null);
            }
        }
Esempio n. 12
0
        private void UpdatePreCull(Camera cam)
        {
            if (lastFrame == Time.frameCount)
            {
                return;
            }
            lastFrame = Time.frameCount;

            InputTracking.GetNodeStates(states);

            for (int i = 0; i < states.Count; i++)
            {
                //Debug.Log("A thing: " + states[i].nodeType + " and " + InputTracking.GetNodeName(states[i].uniqueID));
                if (states[i].nodeType != hand)
                {
                    continue;
                }
                nodeState = states[i];

                var pose = input.GetPose(nodeState);
                transform.localPosition = pose.pos;
                transform.localRotation = pose.rot;

                if (visualization)
                {
                    visualization.SetActive(Tracked = nodeState.tracked);
                }
            }
        }
Esempio n. 13
0
    /// <summary>
    /// Collects the position of the HMD with a timestamp, to be looked up later to correct for latency.
    /// </summary>
    public void CollectPose()
    {
        if (manager == null)
        {
            return;
        }

        KeyPose k = new KeyPose();

#if UNITY_2019_1_OR_NEWER
        InputTracking.GetNodeStates(nodeStates);
        XRNodeState nodeState = nodeStates.Find(node => node.nodeType == XRNode.Head);
        nodeState.TryGetRotation(out k.Orientation);
        nodeState.TryGetPosition(out k.Translation);
#else
        k.Orientation = InputTracking.GetLocalRotation(XRNode.Head);
        k.Translation = InputTracking.GetLocalPosition(XRNode.Head);
#endif

        if (manager.zedCamera.IsCameraReady)
        {
            k.Timestamp = manager.zedCamera.GetCurrentTimeStamp();
            if (k.Timestamp >= 0)
            {
                dllz_latency_corrector_add_key_pose(ref k.Translation, ref k.Orientation, k.Timestamp);                 //Poses are handled by the wrapper.
            }
        }
    }
Esempio n. 14
0
        protected void Direct_ReadState(XRNodeState node)
        {
            if (OpenVR.System == null)
            {
                Debug.LogWarning("OpenVR not active");
                direct_lastState = default(VRControllerState_t);
                return;
            }

            var controllerId = GetDeviceId(node);

            if (controllerId < 0)
            {
                direct_lastState = default(VRControllerState_t);
                return;
            }

            //Debug.Log("Id is " + controllerId);

            var res = OpenVR.System.GetControllerState(
                (uint)controllerId, ref direct_lastState,
                (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VRControllerState_t))
                );

            if (!res)
            {
                Debug.LogWarning("Failed to get controller state");
            }
        }
Esempio n. 15
0
        public static JoyPadType GetJoypadType(XRNodeState node)
        {
            JoyPadType ret;

            if (!nodeTypes.TryGetValue(node.uniqueID, out ret))
            {
                ret = JoyPadType.None;

                if (impl == null)
                {
                    impl = GetImpl();
                }
                var name = impl.GetNodeName(node);

                if (name.Contains("Oculus Touch Controller") || name.StartsWith("Oculus Rift CV1"))
                {
                    //OpenVR gives us "Oculus Rift CV1 (Left Controller)" etc. where I wish it would mention the type of controller (Touch)
                    ret = JoyPadType.Joystick;
                }
                else if (name.StartsWith("Vive Controller"))
                {
                    ret = JoyPadType.TouchPad;
                }
                else
                {
                    Debug.LogWarning("Unknown controller type: " + name);
                }

                nodeTypes[node.uniqueID] = ret;
            }
            return(ret);
        }
Esempio n. 16
0
        private void TryCheckNodeState(TrackingAnchor anchor, XRNode xRNode)
        {
            XRNodeState xRNodeState = default(XRNodeState);

            for (int i = xRNodeStates.Count - 1; i >= 0; i--)
            {
                var nodeState = xRNodeStates[i];
                if (nodeState.nodeType == xRNode)
                {
                    if (nodeState.uniqueID == anchor.uniqueID)
                    {
                        return;
                    }
                    if (xRNodeState.uniqueID == 0)
                    {
                        xRNodeState = nodeState;
                    }
                    if (!anchor.connected)
                    {
                        InputTracking_nodeAdded(anchor, ref nodeState);
                        return;
                    }
                }
            }

            if (anchor.connected)
            {
                InputTracking_nodeRemoved(anchor);
            }
            if (xRNodeState.uniqueID > 0)
            {
                InputTracking_nodeAdded(anchor, ref xRNodeState);
            }
        }
Esempio n. 17
0
        public override float GetAxis(XRNodeState node, InputAxis axis)
        {
            var controller = GetController(node);

            OVRPlugin.ControllerState4 state = OVRPlugin.GetControllerState4((uint)controller);

            switch (axis)
            {
            case InputAxis.LeftClick:
                return(controller == OVRInput.Controller.LTouch ? state.LIndexTrigger : state.RIndexTrigger);

            case InputAxis.RightClick:
                return(controller == OVRInput.Controller.LTouch ? state.LHandTrigger : state.RHandTrigger);

            case InputAxis.MiddleClick: {
                var buttonId = controller == OVRInput.Controller.LTouch ? 0x00000400 : 0x00000004;                 //see enum ovrButton_ in OVR_CAPI.h
                return((state.Buttons & buttonId) != 0 ? 1 : 0);
            }

            case InputAxis.JoyStickX:
            case InputAxis.JoyStickY: {
                var joy = controller == OVRInput.Controller.LTouch ? state.LThumbstick : state.RThumbstick;
                return(axis == InputAxis.JoyStickX ? joy.x : joy.y);
            }

            default:
                return(0);
            }
        }
Esempio n. 18
0
 // handle reconnected device
 private void _OnNodeAdded(XRNodeState obj)
 {
     Debug.Log("Node added " + obj.nodeType);
     if (obj.nodeType == _XRNode)
     {
         _Init();
     }
 }
Esempio n. 19
0
 private void OnTrackingChange(XRNodeState obj)
 {
     if (obj.nodeType == XRNode.RightHand)
     {
         _rightHandTracked = obj.tracked;
         ResetRightHandPointer();
     }
 }
Esempio n. 20
0
 /// <summary>
 /// If the controller is capable, returns if (and sometimes how closely) the player is touching
 /// the given control.
 /// </summary>
 /// <param name="node"></param>
 /// <param name="axis"></param>
 /// <returns></returns>
 public static float GetTouch(XRNodeState node, InputAxis axis)
 {
     if (impl == null)
     {
         impl = GetImpl();
     }
     return(impl.GetTouchValue(node, axis));
 }
Esempio n. 21
0
 // handle reconnected device
 private void _OnNodeAdded(XRNodeState obj)
 {
     if (obj.nodeType == XRNode)
     {
         Debug.Log("Node added " + obj.nodeType);
         _InitDevice();
     }
 }
Esempio n. 22
0
 // handle disonnected device
 private void _OnNodeRemoved(XRNodeState obj)
 {
     if (obj.nodeType == XRNode)
     {
         ShowHandModel(false);
         ShowRayPointer(false);
     }
 }
Esempio n. 23
0
        private void TrackingLost(XRNodeState xrNodeState)
        {
            var job = new TrackJob {
                state = xrNodeState, command = _trackingBarrier.CreateCommandBuffer(), tracked = false
            };

            job.ScheduleSingle(this).Complete();
        }
Esempio n. 24
0
        /// <summary>
        /// Returns where the hand is pointing right now. (z+ forward)
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public virtual Pose GetPose(XRNodeState node)
        {
            Pose ret = new Pose();

            node.TryGetPosition(out ret.pos);
            node.TryGetRotation(out ret.rot);
            return(ret);
        }
Esempio n. 25
0
    void Update()
    {
        if (NodeTitle_Text != null)
        {
            NodeTitle_Text.text = m_Node.ToString();
        }

        var nodeStates = new List <XRNodeState>();

        UnityEngine.XR.InputTracking.GetNodeStates(nodeStates);

        XRNodeState?state = null;

        foreach (XRNodeState nodeState in nodeStates)
        {
            if (nodeState.nodeType == m_Node)
            {
                state = nodeState;
                break;
            }
        }

        if (state.HasValue)
        {
            XRNodeState node = state.Value;
            Vector3     tempVector;
            Quaternion  tempQuaternion;

            // Translation Information
            SetImageColor(Position_Image, node.TryGetPosition(out tempVector));
            Position_Text.text = Vector3ToFieldText(tempVector);
            SetImageColor(Velocity_Image, node.TryGetVelocity(out tempVector));
            Velocity_Text.text = Vector3ToFieldText(tempVector);
            SetImageColor(Acceleration_Image, node.TryGetAcceleration(out tempVector));
            Acceleration_Text.text = Vector3ToFieldText(tempVector);

            // Rotation Information
            SetImageColor(Rotation_Image, node.TryGetRotation(out tempQuaternion));
            Rotation_Text.text = QuaternionToFieldText(tempQuaternion);
            SetImageColor(AngularVelocity_Image, node.TryGetAngularVelocity(out tempVector));
            AngularVelocity_Text.text = Vector3ToFieldText(tempVector);
            SetImageColor(AngularAcceleration_Image, node.TryGetAngularAcceleration(out tempVector));
            AngularAcceleration_Text.text = Vector3ToFieldText(tempVector);
        }
        else
        {
            // Translation Information
            SetImageColor(Position_Image, false);
            SetImageColor(Velocity_Image, false);
            SetImageColor(Acceleration_Image, false);

            // Rotation Information
            SetImageColor(Rotation_Image, false);
            SetImageColor(AngularVelocity_Image, false);
            SetImageColor(AngularAcceleration_Image, false);
        }
    }
Esempio n. 26
0
    public static bool TryGetNodeState(XRNode node, out XRNodeState nodeState)
    {
        List <XRNodeState> nodeStates = new List <XRNodeState>();

        InputTracking.GetNodeStates(nodeStates);
        nodeState = nodeStates.Where(p => p.nodeType == node).FirstOrDefault();

        return(nodeState.tracked);
    }
        /// <summary>
        /// Update the "Controller" input from the device
        /// </summary>
        protected void UpdateControllerData(XRNodeState state)
        {
            Profiler.BeginSample("[MRTK] GenericOpenVRController.UpdateControllerData");

            var lastState = TrackingState;

            LastControllerPose = CurrentControllerPose;

            if (nodeType == XRNode.LeftHand || nodeType == XRNode.RightHand)
            {
                // The source is either a hand or a controller that supports pointing.
                // We can now check for position and rotation.
                IsPositionAvailable   = state.TryGetPosition(out CurrentControllerPosition);
                IsPositionApproximate = false;

                IsRotationAvailable = state.TryGetRotation(out CurrentControllerRotation);

                // Devices are considered tracked if we receive position OR rotation data from the sensors.
                TrackingState = (IsPositionAvailable || IsRotationAvailable) ? TrackingState.Tracked : TrackingState.NotTracked;

                CurrentControllerPosition = MixedRealityPlayspace.TransformPoint(CurrentControllerPosition);
                CurrentControllerRotation = MixedRealityPlayspace.Rotation * CurrentControllerRotation;
            }
            else
            {
                // The input source does not support tracking.
                TrackingState = TrackingState.NotApplicable;
            }

            CurrentControllerPose.Position = CurrentControllerPosition;
            CurrentControllerPose.Rotation = CurrentControllerRotation;

            // Raise input system events if it is enabled.
            if (lastState != TrackingState)
            {
                CoreServices.InputSystem?.RaiseSourceTrackingStateChanged(InputSource, this, TrackingState);
            }

            if (TrackingState == TrackingState.Tracked && LastControllerPose != CurrentControllerPose)
            {
                if (IsPositionAvailable && IsRotationAvailable)
                {
                    CoreServices.InputSystem?.RaiseSourcePoseChanged(InputSource, this, CurrentControllerPose);
                }
                else if (IsPositionAvailable && !IsRotationAvailable)
                {
                    CoreServices.InputSystem?.RaiseSourcePositionChanged(InputSource, this, CurrentControllerPosition);
                }
                else if (!IsPositionAvailable && IsRotationAvailable)
                {
                    CoreServices.InputSystem?.RaiseSourceRotationChanged(InputSource, this, CurrentControllerRotation);
                }
            }

            Profiler.EndSample(); // UpdateControllerData
        }
Esempio n. 28
0
    public static Quaternion?Rotation(this XRNodeState s)
    {
        Quaternion result;

        if (s.TryGetRotation(out result))
        {
            return(result);
        }
        return(null);
    }
        private void InputTracking_trackingAcquired(XRNodeState nodeState)
        {
            XRUserController controller = _userControllers.Find(x => x.UniqueID == nodeState.uniqueID);

            if (controller != null)
            {
                Debug.Log("InputTracking_trackingAcquired " + controller + " " + nodeState.uniqueID + " " + nodeState.nodeType);
                _registrationEvents.TrackingAcquired.Invoke(controller);
            }
        }
Esempio n. 30
0
    void InputTracking_trackingAcquired(XRNodeState obj)
    {
        InputTracking.trackingAcquired -= InputTracking_trackingAcquired;

        defaultHeadRotationInverse = Quaternion.Inverse(mainCam.transform.rotation);

        basisVec   = mainCam.transform.forward;
        basisVec.y = 0.0f;
        basisVec   = basisVec.normalized * radius;
    }