protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { int hitIndex = affectedBodyPart.BodyCoordinateHitIndex(bodyPart, hitLocation); // If -1 is returned, then the hitLocation is not in the CoordinateSpace that the device is attached to if (hitIndex == -1) { return; } // Assign the intensity value for each of the mapped effector for (int n = 0; n < bhapticsMapping.mapping[hitIndex].indexMapping.Length; n++) { // The intensity needs to be between 0 and 100, so convert the intensity to that range hapticBytes[bhapticsMapping.mapping[hitIndex].indexMapping[n]] = (byte)(intensity * 100); } // Assign the intensity values and play #if VRTK_DEFINE_SDK_BHAPTICS tactSource.DotPoints = hapticBytes; tactSource.Play(); #endif // Clear the array since it will need different values on the next frame hapticBytes = new byte[HAPTIC_ACTUATOR_COUNT]; }
/// <summary> /// Provides the hit location (angle and height) values for all the curve values in the pattern for a specified time /// </summary> /// <param name="currentTime">The timing information</param> /// <returns>An array of BodyCoordinateHit value for each curve in the pattern</returns> public BodyCoordinateHit[] GetHitLocationsAtCurrentTime(float currentTime) { // Get the hit location for each curve for (int n = 0; n < curveList.Count; n++) { BodyCoordinateHit hitLocation = curveList[n].GetHitLocationAtTime(currentTime); // Adjust the hitLocation based on the offset switch (offsetUse) { case OffsetUse.SetAtFirstPoint: // Get the difference between the offset and the first point float heightDifference = hitOffset.hitHeight - curveList[n].heightCurve[0].value; float angleDifference = hitOffset.hitAngle - curveList[n].angleCurve[0].value; // Apply the difference to the current hit location hitLocation.hitHeight += heightDifference; hitLocation.hitAngle += angleDifference; break; } // Adjust the hitLocation based on the overshoot resolution for the height hitLocation.hitHeight = AdjustHitLocation(hitLocation.hitHeight, 0.0f, 1.0f, heightOvershootResolution); // Adjust the hitLocation based on the overshoot resolution for the angle hitLocation.hitAngle = AdjustHitLocation(hitLocation.hitAngle, 0.0f, 360.0f, angleOvershootResolution); hitLocationBuffer[n] = hitLocation; } return(hitLocationBuffer); }
/// <summary> /// Checks to see if the given body part that was hit has a device that should be triggered. If it does, /// it tells that device to play their haptic for that hit location. /// </summary> /// <param name="bodyLocation">The body part that was hit</param> /// <param name="hitLocation">The height/angle of the hit on the body part</param> public void BodyPartHit(HumanBodyBones bodyLocation, BodyCoordinateHit hitLocation, ScriptableHapticPattern hapticPattern = null) { // Check to make sure this body part isn't ignored if (ignoreBoneSet.Contains(bodyLocation)) { return; } if (bodyAffectedByDevice.ContainsKey(bodyLocation)) { // Tell every device that has this body part to trigger the haptics at the given location for (int n = 0; n < bodyAffectedByDevice[bodyLocation].Count; n++) { // Check to make sure this haptic device isn't ignored if (ignoreHapticDevices.Contains(bodyAffectedByDevice[bodyLocation][n])) { return; } if (hapticPattern == null) { bodyAffectedByDevice[bodyLocation][n].TriggerDevice(bodyLocation, hitLocation, 0.5f); } else { bodyAffectedByDevice[bodyLocation][n].TriggerDevice(bodyLocation, hitLocation, hapticPattern); } } } }
/// <summary> /// A single haptic pulse that is triggered by an object hitting a body part that has been /// set up to recieve haptic events. /// </summary> /// <param name="bodyPart">The body part of the hit</param> /// <param name="hitLocation">The body coordinate system hit</param> /// <param name="intensity">The intensity to play the haptic vibration</param> public void TriggerDevice(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { if (affectedBodyPart.HitInsideAffectedArea(bodyPart, hitLocation)) { StartHaptics(bodyPart, hitLocation, intensity); } }
public void OnBodyPartHit(BodyCoordinate bodyCoordinate, BodyCoordinateHit hitInfo, Vector3 hitLocation) { if (BodyPartHit != null) { BodyPartHit(this, new HapticInformation(bodyCoordinate, hitInfo, hitLocation)); } }
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { #if VRTK_DEFINE_STEAMVR_PLUGIN_2_0_0_OR_NEWER && VRTK_DEFINE_STEAMVR_INPUT_COMPILED vibration.Execute(0.0f, hapticDuration / Constants.MS_TO_SECONDS, frequency, intensity, trackedObject.inputSource); #elif VRTK_DEFINE_STEAMVR_PLUGIN_LEGACY SteamVR_Controller.Input((int)trackedObject.index).TriggerHapticPulse((ushort)(intensity * VIVE_VIBRATION_VALUE)); #endif }
/// <summary> /// Plays a pattern that is triggered by an object hitting a body part that has been /// set up to recieve haptic events. /// </summary> /// <param name="bodyPart">The body part of the hit</param> /// <param name="hitLocation">The body coordinate system hit</param> /// <param name="hapticPattern">The haptic pattern</param> public void TriggerDevice(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, ScriptableHapticPattern hapticPattern) { if (bodyPart == affectedBodyPart.affectableBodyParts) { hapticPattern.hitOffset = hitLocation; PlayPattern(bodyPart, hapticPattern); } }
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { #if VRTK_DEFINE_SDK_MANUS_VR Manus.ManusSetVibration(handData.Session, isRightGlove ? device_type_t.GLOVE_RIGHT : device_type_t.GLOVE_LEFT, intensity, (ushort)hapticDuration); #endif }
private void OnCollisionEnter(Collision collision) { BodyCoordinateHit bodyHit = CalculateBodyCoordinateHitFromPosition(collision.contacts[0].point); HapticPatternCollisionHolder hitPattern = collision.gameObject.GetComponent <HapticPatternCollisionHolder>(); if (hitPattern != null) { manager.BodyPartHit(attachedBody, bodyHit, hitPattern.GetHapticPattern()); } else { manager.BodyPartHit(attachedBody, bodyHit); } }
public bool HitInsideSpace(BodyCoordinateHit hitLocation) { if (hitLocation.hitHeight >= startHeight && hitLocation.hitHeight <= endHeight && hitLocation.hitAngle >= startAngle && hitLocation.hitAngle <= endAngle) { return(true); } else { return(false); } }
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { #if VRTK_DEFINE_SDK_SENSE_GLOVE int fingerIndex = FindHitFingerIndex(bodyPart); if (fingerIndex != -1) { fingers[fingerIndex] = true; intensities[fingerIndex] = (int)(intensity * 100); duration[fingerIndex] = hapticDuration; } glove.SendBuzzCmd(fingers, intensities, duration); #endif }
public int BodyCoordinateHitIndex(HumanBodyBones hitBone, BodyCoordinateHit hitLocation) { // Check if it's on the same bone if (hitBone == affectableBodyParts) { for (int n = 0; n < affectedAreaList.Count; n++) { // Check if it's an affect area on the same bone if (affectedAreaList[n].HitInsideSpace(hitLocation)) { return(n); } } } return(-1); }
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { #if VRTK_DEFINE_SDK_OCULUS if (isLeftController) { hapticsClipLeft.Reset(); hapticsClipLeft.WriteSample((byte)(intensity * byte.MaxValue)); OVRHaptics.LeftChannel.Preempt(hapticsClipLeft); } else { hapticsClipRight.Reset(); hapticsClipRight.WriteSample((byte)(intensity * byte.MaxValue)); OVRHaptics.RightChannel.Preempt(hapticsClipRight); } #endif }
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { if (IsHapticDeviceValid()) { HapticCapabilities hapticCapabilities; if (hapticDevice.TryGetHapticCapabilities(out hapticCapabilities) && hapticCapabilities.supportsImpulse) { bool successful = hapticDevice.SendHapticImpulse(hapticChannel, intensity); if (!successful) { Debug.LogWarningFormat("Unity XR Haptic Device {0} failed to play haptics"); } } } else { Debug.LogWarningFormat("Unity XR Haptic Device for the {0} failed to play haptics: device is invalid", handNodeType.ToString()); } }
/// <summary> /// Calculates the world position of a BodyCoordinateHit. /// </summary> /// <param name="bodyHit">The height and angle of the collider to find the position</param> /// <param name="accountForCapsule">If true, the position should account for the circle ends on the capsule</param> /// <returns>The world position based on the collider and provided body hit</returns> public Vector3 CalculatePositionFromBodyCoordinateHit(BodyCoordinateHit bodyHit, bool accountForCapsule = false) { float heightPosition = CalculatePositionFromNormalizedHeight(bodyHit.hitHeight); Vector3 radius = CalculatePositionFromAngle(bodyHit, heightPosition, accountForCapsule); switch (capCollider.direction) { // X Axis case 0: return(new Vector3(heightPosition, radius.y, radius.z)); // Y Axis case 1: return(new Vector3(radius.x, heightPosition, radius.z)); // Z Axis case 2: return(new Vector3(radius.x, radius.y, heightPosition)); default: return(Vector3.zero); } }
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { #if VRTK_DEFINE_SDK_OCULUS if (hapticsClipLeft == null || hapticsClipRight == null) { OVRHaptics.Config.Load(); hapticsClipLeft = new OVRHapticsClip(); hapticsClipRight = new OVRHapticsClip(); } int clipLengthBytes = Mathf.Min( Mathf.Max(OVRHaptics.Config.SampleRateHz * hapticDuration, OVRHaptics.Config.MinimumBufferSamplesCount), OVRHaptics.Config.MaximumBufferSamplesCount ); if (isLeftController) { hapticsClipLeft.Reset(); for (int i = 0; i < clipLengthBytes; i++) { hapticsClipLeft.WriteSample((byte)(intensity * byte.MaxValue)); } OVRHaptics.LeftChannel.Preempt(hapticsClipLeft); } else { hapticsClipRight.Reset(); for (int i = 0; i < clipLengthBytes; i++) { hapticsClipRight.WriteSample((byte)(intensity * byte.MaxValue)); } OVRHaptics.RightChannel.Preempt(hapticsClipRight); } #endif }
/// <summary> /// How the device plays a single haptic pulse /// </summary> /// <param name="hitLocation">The hit location in the BodyCoordinate space</param> /// <param name="intensity">The normalized (0-1) intensity value</param> protected abstract void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity);
protected override void StartHaptics(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, float intensity) { // no op }
public void GetMouseClicks(SceneView sceneView) { // Only care about mouse down events if (Event.current.type != EventType.MouseDown) { return; } // convert GUI coordinates to screen coordinates Vector3 screenPosition = Event.current.mousePosition; Vector3 cameraScreenPosition = screenPosition; cameraScreenPosition.y = Camera.current.pixelHeight - cameraScreenPosition.y; Ray ray = Camera.current.ScreenPointToRay(cameraScreenPosition); RaycastHit hit; // Wait for double clicks on left mouse if (Event.current.clickCount == 2 && Event.current.button == 0) { // Cast ray into the scene if (Physics.Raycast(ray, out hit)) { Event.current.Use(); BodyCoordinate bodyPart = hit.collider.GetComponent <BodyCoordinate>(); if (bodyPart != null) { BodyCoordinateHit hitLocation = bodyPart.CalculateBodyCoordinateHitFromPosition(hit.point); OnBodyPartHit(bodyPart, hitLocation, hit.point); return; } BodyHitUI uiHit = hit.collider.GetComponent <BodyHitUI>(); if (uiHit != null) { // Make sure that another UI is not already displaying lastUIHit?.HideUI(screenPosition, true); lastUIHit = uiHit; lastUIHit.DisplayUI(screenPosition); return; } } } else if (Event.current.clickCount == 1) { // Close the hitUI if they click outside of it if (lastUIHit != null) { // Right/middle clicks will force close the UI bool wasHidden = (Event.current.button != 0) ? lastUIHit.HideUI(screenPosition, true) : lastUIHit.HideUI(screenPosition); if (wasHidden) { lastUIHit = null; } } } }
public BodyCoordinateHit(BodyCoordinateHit otherHit) { hitHeight = otherHit.hitHeight; hitAngle = otherHit.hitAngle; }
/// <summary> /// Adds a keyframe to the Haptic Pattern at the specified time. /// </summary> /// <param name="time">The time for the keyframe</param> /// <param name="location">The hit angle and height</param> /// <param name="intensity">The intensity between 0 and 1</param> public void AddKey(float time, BodyCoordinateHit location, float intensity) { heightCurve.AddKey(time, location.hitHeight); angleCurve.AddKey(time, location.hitAngle); intensityCurve.AddKey(time, intensity); }
private Vector3 CalculatePositionFromAngle(BodyCoordinateHit bodyHit, float heightPosition = 0.0f, bool positionOnCapsule = false) { // Short circuit, if the height is at the extremes, then it will be at the poles if (positionOnCapsule) { if (bodyHit.hitHeight == 0.0f) { return(transform.TransformPoint(ProximalPosition)); } else if (bodyHit.hitHeight == 1.0f) { return(transform.TransformPoint(DistalPosition)); } } // Calculate the directional vector towards the hit point Vector3 rotationDirection = Vector3.zero; if (invertAngleDirection) { bodyHit.hitAngle = 360 - bodyHit.hitAngle; } switch (capCollider.direction) { // X Axis case 0: rotationDirection = Quaternion.AngleAxis(bodyHit.hitAngle, transform.right) * polarAxis; break; // Y Axis case 1: rotationDirection = Quaternion.AngleAxis(bodyHit.hitAngle, transform.up) * polarAxis; break; // Z Axis case 2: rotationDirection = Quaternion.AngleAxis(bodyHit.hitAngle, transform.forward) * polarAxis; break; } // Account for the angle of the collider Vector3 positionByAngle = rotationDirection * capCollider.radius; // Since the capsule has two circles at the ends, make sure the position is along the curve on the circle if required if (positionOnCapsule) { // Don't bother doing this if new height is not at one of the circles extremes float radiusNormalizedPerHeight = capCollider.radius / capCollider.height; if (bodyHit.hitHeight >= radiusNormalizedPerHeight && bodyHit.hitHeight <= (1 - radiusNormalizedPerHeight)) { return(transform.TransformPoint(positionByAngle)); } // Get the transform that is aligned with this point along the axis Vector3 leveledTransform = transform.TransformPoint(capCollider.center); switch (capCollider.direction) { case 0: // X Axis leveledTransform = new Vector3(heightPosition, leveledTransform.y, leveledTransform.z); positionByAngle = new Vector3(heightPosition, positionByAngle.y, positionByAngle.z); break; case 1: // Y Axis leveledTransform = new Vector3(leveledTransform.x, heightPosition, leveledTransform.z); positionByAngle = new Vector3(positionByAngle.x, heightPosition, positionByAngle.z); break; case 2: // Z Axis leveledTransform = new Vector3(leveledTransform.x, leveledTransform.y, heightPosition); positionByAngle = new Vector3(positionByAngle.x, positionByAngle.y, heightPosition); break; } // Get the direction from the point towards the transform Vector3 directionTowardsCapsule = leveledTransform - positionByAngle; RaycastHit[] hitInfo; // Shoot a ray to find the edge of the capsule collider in the calculated direction hitInfo = Physics.RaycastAll(positionByAngle, directionTowardsCapsule.normalized, .5f); for (int n = 0; n < hitInfo.Length; n++) { if (hitInfo[n].collider.gameObject.name == "Capsule") { // Move the positionByAngle in the direction towards the transform at the proper distance positionByAngle += (directionTowardsCapsule.normalized * hitInfo[n].distance); } } } return(transform.TransformPoint(positionByAngle)); }
private void OnDisable() { // Don't keep a saved offset value since it's only set by a collision hitOffset = new BodyCoordinateHit(); }
/// <summary> /// Adds a keyframe to the pattern for a given curve /// </summary> /// <param name="curveIndex">The curve index to add the keyframe to</param> /// <param name="time">The timing for the new keyframe</param> /// <param name="location">The height/angle of the keyframe</param> /// <param name="intensity">The intensity value of the keyframe</param> public void AddKey(int curveIndex, float time, BodyCoordinateHit location, float intensity) { curveList[curveIndex].AddKey(time, location, intensity); }
public HapticFeedbackEventArgs(HumanBodyBones body, BodyCoordinateHit hit, float intense) { bodyPart = body; hitLocation = hit; feedbackIntensity = intense; }
public HapticInformation(BodyCoordinate bodyCoordinate, BodyCoordinateHit hitInfo, Vector3 hitLocation) { bodyPart = bodyCoordinate; bodyHitInfo = hitInfo; worldHitLocation = hitLocation; }