//用于计算左手/右手腕关节姿态(由动捕设备得到)和左手手背姿态(由数据手套得到)之间的四元数差值,该方法为动态调用,即每一帧都会调用该计算。 //适用于:当动捕设备有腕关节/手背节点时 private Quaternion CalculateDynamicOffset(GameObject tracker, VRTRIXDataWrapper glove, HANDTYPE type) { //计算场景中角色右手腕在unity世界坐标系下的旋转与手套的右手腕在手套追踪系统中世界坐标系下右手腕的旋转之间的角度差值,意在匹配两个坐标系的方向; if (type == HANDTYPE.RIGHT_HAND) { Quaternion rotation = glove.GetReceivedRotation(VRTRIXBones.R_Hand); Vector3 quat_vec = mr_axisoffset.MultiplyVector(new Vector3(rotation.x, rotation.y, rotation.z)); rotation = new Quaternion(quat_vec.x, quat_vec.y, quat_vec.z, rotation.w); Quaternion target = tracker.transform.rotation * qroffset * Quaternion.Euler(0, -90, 90); return(target * Quaternion.Inverse(rotation)); } //计算场景中角色左手腕在unity世界坐标系下的旋转与手套的左手腕在手套追踪系统中世界坐标系下左手腕的旋转之间的角度差值,意在匹配两个坐标系的方向; else if (type == HANDTYPE.LEFT_HAND) { Quaternion rotation = glove.GetReceivedRotation(VRTRIXBones.L_Hand); Vector3 quat_vec = ml_axisoffset.MultiplyVector(new Vector3(rotation.x, rotation.y, rotation.z)); rotation = new Quaternion(quat_vec.x, quat_vec.y, quat_vec.z, rotation.w); Quaternion target = tracker.transform.rotation * qloffset * Quaternion.Euler(0, 90, -90); return(target * Quaternion.Inverse(rotation)); } else { return(Quaternion.identity); } }
void Start() { if (!IsEnableMultipleGloves) { Index = GloveIndex.MaxDeviceCount; } LH = new VRTRIXDataWrapper(AdvancedMode, (int)Index, version); RH = new VRTRIXDataWrapper(AdvancedMode, (int)Index, version); GloveGesture = new VRTRIXGloveGestureRecognition(); fingerTransformArray = FindFingerTransform(); for (int i = 0; i < 3; i++) { ml_axisoffset.SetRow(i, ql_axisoffset[i]); mr_axisoffset.SetRow(i, qr_axisoffset[i]); } ml_axisoffset.SetRow(3, Vector3.forward); mr_axisoffset.SetRow(3, Vector3.forward); if (IsVREnabled) { try { RH_tracker = CheckDeviceModelName(HANDTYPE.RIGHT_HAND); LH_tracker = CheckDeviceModelName(HANDTYPE.LEFT_HAND); } catch (Exception e) { print("Exception caught: " + e); } } }
public void RegisterCallBack() { receivedDataCallback = (IntPtr pUserParam, IntPtr ptr, int data_rate, byte radio_strength, IntPtr cal_score_ptr, float battery, int hand_type, int radio_channel) => { GCHandle handle_consume = (GCHandle)pUserParam; VRTRIXDataWrapper objDataGlove = (handle_consume.Target as VRTRIXDataWrapper); VRTRIX_Quat[] quat = new VRTRIX_Quat[16]; MarshalUnmananagedArray2Struct <VRTRIX_Quat>(ptr, 16, out quat); for (int i = 0; i < 16; i++) { objDataGlove.data[i] = new Quaternion(quat[i].qx, quat[i].qy, quat[i].qz, quat[i].qw); } objDataGlove.data_rate = data_rate; objDataGlove.radio_strength = radio_strength; objDataGlove.battery = battery; objDataGlove.hand_type = (HANDTYPE)hand_type; objDataGlove.radio_channel = radio_channel; Marshal.Copy(cal_score_ptr, objDataGlove.calscore, 0, 6); }; receivedEventCallback = (IntPtr pUserParam, IntPtr pEvent) => { GCHandle handle_consume = (GCHandle)pUserParam; VRTRIXDataWrapper objDataGlove = (handle_consume.Target as VRTRIXDataWrapper); VRTRIXGloveEvent gloveEvent = (VRTRIXGloveEvent)pEvent; if (gloveEvent == VRTRIXGloveEvent.VRTRIXGloveEvent_Connected) { objDataGlove.stat = VRTRIXGloveStatus.NORMAL; Debug.Log("VRTRIXGloveEvent_Connected"); } else if (gloveEvent == VRTRIXGloveEvent.VRTRIXGloveEvent_Disconnected) { objDataGlove.stat = VRTRIXGloveStatus.DISCONNECTED; Debug.Log("VRTRIXGloveEvent_Disconnected"); } else if (gloveEvent == VRTRIXGloveEvent.VRTRIXGloveEvent_ChannelHopping) { Debug.Log("VRTRIXGloveEvent_ChannelHopping"); } }; if (sp != IntPtr.Zero) { GCHandle handle_reg = GCHandle.Alloc(this); IntPtr pUserParam = (IntPtr)handle_reg; RegisterDataCallback(pUserParam, this.sp, receivedDataCallback); RegisterEventCallback(this.sp, receivedEventCallback); } }
// Use this for initialization void Start() { RH = new VRTRIXDataWrapper(AdvancedMode); LH = new VRTRIXDataWrapper(AdvancedMode); try { RH_tracker = CheckDeviceModelName(HANDTYPE.RIGHT_HAND); LH_tracker = CheckDeviceModelName(HANDTYPE.LEFT_HAND); } catch (Exception e) { print("Exception caught: " + e); } }
private static Quaternion GetOffset(GameObject objectToAlign, VRTRIXDataWrapper glove, HANDTYPE type) { //print("IMU: " + glove.GetReceivedRotation(VRTRIXBones.L_Hand).eulerAngles.y); //print("objectToAlign: " + objectToAlign.transform.rotation.eulerAngles.y); float offset = glove.GetReceivedRotation(VRTRIXBones.L_Hand).eulerAngles.y - objectToAlign.transform.rotation.eulerAngles.y - 90f; if (offset > 180f) { offset = 360f - offset; } else if (offset < -180f) { offset = 360f + offset; } //print(offset); return(new Quaternion(0, Mathf.Sin(-offset * degToRad / 2), 0, Mathf.Cos(-offset * degToRad / 2))); }
//数据手套反初始化,硬件断开连接 //! Disconnect data glove and uninitialization. public void OnDisconnectGlove() { if (LH_Mode) { if (LH.ClosePort()) { LH = new VRTRIXDataWrapper(AdvancedMode, (int)Index, version); } LH_Mode = false; } if (RH_Mode) { if (RH.ClosePort()) { RH = new VRTRIXDataWrapper(AdvancedMode, (int)Index, version); } RH_Mode = false; } }
//用于计算初始化物体的姿态和手背姿态(由数据手套得到)之间的四元数差值,该方法为静态调用,即只在初始化的时候调用一次,之后所有帧均使用同一个四元数。 //适用于:当动捕设备没有腕关节/手背节点或者只单独使用手套,无其他定位硬件设备时。 private Quaternion CalculateStaticOffset(VRTRIXDataWrapper glove, HANDTYPE type) { if (type == HANDTYPE.RIGHT_HAND) { if (IsVREnabled) { float angle_offset = RH_tracker.transform.rotation.eulerAngles.z; return(Quaternion.AngleAxis(-angle_offset, Vector3.forward)); } else { Quaternion rotation = glove.GetReceivedRotation(VRTRIXBones.R_Hand); Vector3 quat_vec = mr_axisoffset.MultiplyVector(new Vector3(rotation.x, rotation.y, rotation.z)); rotation = new Quaternion(quat_vec.x, quat_vec.y, quat_vec.z, rotation.w); return(RH_ObjectToAlign.transform.rotation * Quaternion.Inverse(rotation)); } } else if (type == HANDTYPE.LEFT_HAND) { if (IsVREnabled) { float angle_offset = LH_tracker.transform.rotation.eulerAngles.z; return(Quaternion.AngleAxis(-angle_offset, Vector3.forward)); } else { Quaternion rotation = glove.GetReceivedRotation(VRTRIXBones.L_Hand); Vector3 quat_vec = ml_axisoffset.MultiplyVector(new Vector3(rotation.x, rotation.y, rotation.z)); rotation = new Quaternion(quat_vec.x, quat_vec.y, quat_vec.z, rotation.w); return(LH_ObjectToAlign.transform.rotation * Quaternion.Inverse(rotation)); } } else { return(Quaternion.identity); } }
void Start() { RH = new VRTRIXDataWrapper(AdvancedMode); LH = new VRTRIXDataWrapper(AdvancedMode); LastFeedbackTime = Time.time; }
public static VRTRIXGloveGesture GestureDetection(VRTRIXDataWrapper hand, HANDTYPE type) { if (type == HANDTYPE.LEFT_HAND) { float ThumbAngle = hand.GetReceivedGestureAngle(VRTRIXBones.L_Thumb_2); float IndexAngle = hand.GetReceivedGestureAngle(VRTRIXBones.L_Index_2); float MiddleAngle = hand.GetReceivedGestureAngle(VRTRIXBones.L_Middle_2); float RingAngle = hand.GetReceivedGestureAngle(VRTRIXBones.L_Ring_2); float PinkyAngle = hand.GetReceivedGestureAngle(VRTRIXBones.L_Pinky_2); bool ThumbCurve = (ThumbAngle < 10f) || (ThumbAngle > 170f); bool IndexCurve = (IndexAngle < 270f) && (IndexAngle > 95f); bool MiddleCurve = (MiddleAngle < 270f) && (MiddleAngle > 95f); bool RingCurve = (RingAngle < 270f) && (RingAngle > 95f); bool PinkyCurve = (PinkyAngle < 270f) && (PinkyAngle > 95f); bool TeleportCheck = (RingAngle - MiddleAngle < -110f || RingAngle - MiddleAngle > 200) && (PinkyAngle - IndexAngle <-110f || PinkyAngle - IndexAngle> 200f); bool PaperCheck = (IndexAngle < 30f && MiddleAngle < 30f && RingAngle < 30f && PinkyAngle < 30f && (ThumbAngle > 30f && ThumbAngle < 90f)); //Debug.Log("ThumbAngle: " + ThumbAngle + ", IndexAngle: " + IndexAngle + ", MiddleAngle: " + MiddleAngle + ", RingAngle: " + RingAngle + ", PinkyAngle: " + PinkyAngle); //Debug.Log("TeleportCheck1: " + (RingAngle - MiddleAngle) + ", TeleportCheck2: " + (PinkyAngle - IndexAngle)); if (ThumbCurve && MiddleCurve && RingCurve && PinkyCurve && !IndexCurve) { return(VRTRIXGloveGesture.BUTTONCLICK); } if (ThumbCurve && RingCurve && PinkyCurve && !IndexCurve && !MiddleCurve && TeleportCheck) { return(VRTRIXGloveGesture.BUTTONTELEPORT); } if (ThumbCurve && IndexCurve && !MiddleCurve && !RingCurve && !PinkyCurve) { return(VRTRIXGloveGesture.BUTTONOK); } if (ThumbCurve && MiddleCurve && RingCurve && PinkyCurve && IndexCurve) { return(VRTRIXGloveGesture.BUTTONGRAB); } if (!ThumbCurve && !MiddleCurve && !RingCurve && !PinkyCurve && !IndexCurve && PaperCheck) { return(VRTRIXGloveGesture.BUTTONPAPER); } } else if (type == HANDTYPE.RIGHT_HAND) { float ThumbAngle = hand.GetReceivedGestureAngle(VRTRIXBones.R_Thumb_2); float IndexAngle = hand.GetReceivedGestureAngle(VRTRIXBones.R_Index_2); float MiddleAngle = hand.GetReceivedGestureAngle(VRTRIXBones.R_Middle_2); float RingAngle = hand.GetReceivedGestureAngle(VRTRIXBones.R_Ring_2); float PinkyAngle = hand.GetReceivedGestureAngle(VRTRIXBones.R_Pinky_2); bool ThumbCurve = (ThumbAngle < 10f) || (ThumbAngle > 170f); bool IndexCurve = (IndexAngle < 270f) && (IndexAngle > 95f); bool MiddleCurve = (MiddleAngle < 270f) && (MiddleAngle > 95f); bool RingCurve = (RingAngle < 270f) && (RingAngle > 95f); bool PinkyCurve = (PinkyAngle < 270f) && (PinkyAngle > 95f); bool TeleportCheck = (RingAngle - MiddleAngle < -80f || RingAngle - MiddleAngle > 200) && (PinkyAngle - IndexAngle <-90f || PinkyAngle - IndexAngle> 210f); bool PaperCheck = (IndexAngle < 30f && MiddleAngle < 30f && RingAngle < 30f && PinkyAngle < 30f && (ThumbAngle > 30f && ThumbAngle < 90f)); //Debug.Log("ThumbAngle: " + ThumbAngle + ", IndexAngle: " + IndexAngle + ", MiddleAngle: " + MiddleAngle + ", RingAngle: " + RingAngle + ", PinkyAngle: " + PinkyAngle); //Debug.Log("ThumbAngle: " + ThumbAngle + ", ThumbCurve: " + ThumbCurve); if (ThumbCurve && MiddleCurve && RingCurve && PinkyCurve && !IndexCurve) { return(VRTRIXGloveGesture.BUTTONCLICK); } if (ThumbCurve && RingCurve && PinkyCurve && !IndexCurve && !MiddleCurve && TeleportCheck) { return(VRTRIXGloveGesture.BUTTONTELEPORT); } if (ThumbCurve && IndexCurve && !MiddleCurve && !RingCurve && !PinkyCurve) { return(VRTRIXGloveGesture.BUTTONOK); } if (ThumbCurve && MiddleCurve && RingCurve && PinkyCurve && IndexCurve) { return(VRTRIXGloveGesture.BUTTONGRAB); } if (!ThumbCurve && !MiddleCurve && !RingCurve && !PinkyCurve && !IndexCurve && PaperCheck) { return(VRTRIXGloveGesture.BUTTONPAPER); } } return(VRTRIXGloveGesture.BUTTONNONE); }