protected virtual void PaintCovexHull(HandData hand, Graphics g) { if (hand.ConvexHull.Count > 3) { g.DrawLines(Pens.White, hand.ConvexHull.Points.Select(p => new System.Drawing.Point((int)p.X, (int)p.Y)).ToArray()); } }
/// <summary> /// Updates <see cref="HandData.PointerPose"/> if the platform did not provide it. /// </summary> /// <param name="handData">The hand data to update <see cref="HandData.PointerPose"/> for.</param> private HandData UpdatePointerPose(HandData handData) { if (handData.TrackingState == TrackingState.Tracked && !PlatformProvidesPointerPose) { var palmPose = handData.Joints[(int)TrackedHandJoint.Palm]; palmPose.Rotation = Quaternion.Inverse(palmPose.Rotation) * palmPose.Rotation; var thumbProximalPose = handData.Joints[(int)TrackedHandJoint.ThumbProximal]; var indexDistalPose = handData.Joints[(int)TrackedHandJoint.IndexDistal]; var pointerPosition = handData.RootPose.Position + Vector3.Lerp(thumbProximalPose.Position, indexDistalPose.Position, .5f); var pointerEndPosition = pointerPosition + palmPose.Forward * 10f; var camera = MixedRealityToolkit.CameraSystem != null ? MixedRealityToolkit.CameraSystem.MainCameraRig.PlayerCamera : CameraCache.Main; var pointerDirection = pointerEndPosition - pointerPosition; var pointerRotation = Quaternion.LookRotation(pointerDirection, camera.transform.up) * handData.RootPose.Rotation; pointerRotation = camera.transform.rotation * pointerRotation; handData.PointerPose = new MixedRealityPose(pointerPosition, pointerRotation); } return(handData); }
/// <summary> /// Updates <see cref="HandData.IsPinching"/> and <see cref="HandData.PinchStrength"/> /// if the platform did not provide it. /// </summary> /// <param name="handData">The hand data to update <see cref="HandData.IsPinching"/> and <see cref="HandData.PinchStrength"/> for.</param> private HandData UpdateIsPinchingAndStrength(HandData handData) { if (handData.TrackingState == TrackingState.Tracked) { var thumbTipPose = handData.Joints[(int)TrackedHandJoint.ThumbTip]; var indexTipPose = handData.Joints[(int)TrackedHandJoint.IndexTip]; if (!PlatformProvidesIsPinching) { handData.IsPinching = (thumbTipPose.Position - indexTipPose.Position).sqrMagnitude < TWO_CENTIMETER_SQUARE_MAGNITUDE; } if (!PlatformProvidesPinchStrength) { var distanceSquareMagnitude = (thumbTipPose.Position - indexTipPose.Position).sqrMagnitude - TWO_CENTIMETER_SQUARE_MAGNITUDE; handData.PinchStrength = 1 - Mathf.Clamp(distanceSquareMagnitude / PINCH_STRENGTH_DISTANCE, 0f, 1f); } } else { handData.IsPinching = false; handData.PinchStrength = 0f; } return(handData); }
/// <summary> /// Updates <see cref="HandData.IsPointing"/> if the platform did not provide it. /// </summary> /// <param name="handData">The hand data to update <see cref="HandData.IsPointing"/> for.</param> private HandData UpdateIsPointing(HandData handData) { if (handData.TrackingState == TrackingState.Tracked && !PlatformProvidesIsPointing) { var playspaceTransform = MixedRealityToolkit.CameraSystem.MainCameraRig.PlayspaceTransform; var localPalmPose = handData.Joints[(int)TrackedHandJoint.Palm]; var worldPalmPose = new MixedRealityPose() { Position = handData.RootPose.Position + handData.RootPose.Rotation * localPalmPose.Position, Rotation = playspaceTransform.rotation * handData.RootPose.Rotation * localPalmPose.Rotation }; var cameraTransform = MixedRealityToolkit.CameraSystem != null ? MixedRealityToolkit.CameraSystem.MainCameraRig.PlayerCamera.transform : CameraCache.Main.transform; // We check if the palm forward is roughly in line with the camera lookAt. var projectedPalmUp = Vector3.ProjectOnPlane(-worldPalmPose.Up, cameraTransform.up); handData.IsPointing = Vector3.Dot(cameraTransform.forward, projectedPalmUp) > IS_POINTING_DOTP_THRESHOLD; } else { handData.IsPointing = false; } return(handData); }
void GiveAnalysis(HandData data) { //show results debug.text += data.fingerAmount + " : " + data.handSize + " : " + data.handTakeOffTime; Result bestMatch = new Result(); float differenceAmount = 99; foreach (Result result in resultList.allResults) { float newMatchValue = 0; newMatchValue += Mathf.Abs(data.handSize - result.requiredHandSize * 0.75f); newMatchValue += Mathf.Abs(data.handTakeOffTime - result.requiredSpeed * 0.50f); if (newMatchValue < differenceAmount) { debug.text += " next best = " + result.resultName; differenceAmount = newMatchValue; bestMatch = result; } } debug.text += bestMatch.resultName; resultFolder.GetComponentInChildren <Text>().text = bestMatch.resultName; Invoke("BackToIdle", 5); }
IEnumerator Timer() { yield return(new WaitForSeconds(analyzeTime)); if (!released) { float newTakeOffTime = Time.time - handTouchTime; currenthand.handTakeOffTime = newTakeOffTime / analyzeTime; debug.text += " Release!! "; released = true; } state = State.results; lastHands.Add(CompareWithOtherHandData(currenthand)); currenthand = new HandData(); debug.text += " Analysis complete :: HandData: -> "; GiveAnalysis(lastHands[lastHands.Count - 1]); analyzeReadyScreen.SetActive(true); yield return(new WaitForSeconds(1f)); analyzeReadyScreen.SetActive(false); }
protected virtual void DrawFingerPoints(HandData hand, Graphics g) { foreach (var point in hand.FingerPoints) { PaintFingerPoint(g, point); } }
bool FilterPointObject(ref HandData hand, Rigidbody rigidBody) { if (!rigidBody) { return(false); } GameObject gameObject = rigidBody.gameObject; if (!gameObject) { return(false); } if (gameObject.layer != contextLayerCubes && gameObject.layer != contextLayerGrip) { return(false); } NetworkInfo networkInfo = gameObject.GetComponent <NetworkInfo>(); if (!networkInfo) { return(false); } return(true); }
protected virtual void DrawFingerPoints(HandData cluster, DrawingContext drawingContext) { foreach (var point in cluster.FingerPoints) { PaintFingerPoint(point, drawingContext); } }
public void CubeDetached(ref HandData hand) { var rigidBody = hand.gripObject.GetComponent <Rigidbody>(); rigidBody.isKinematic = false; hand.gripObject.layer = contextLayerCubes; CalculateAndApplyReleaseVelocity(ref hand, rigidBody, hand.disableReleaseVelocity); hand.gripObject.transform.SetParent(null); hand.gripObject = null; if (rigidBody.position.y < MinimumCubeHeight) { Vector3 position = rigidBody.position; position.y = MinimumCubeHeight; rigidBody.position = position; } if (hand.gripObjectSupportList != null) { WakeUpObjects(hand.gripObjectSupportList); hand.gripObjectSupportList = null; } hand.pointObject = null; hand.pointObjectFrame = 0; hand.gripObjectReleaseFrame = context.GetRenderFrame(); }
void AttachToHand(ref HandData hand, GameObject gameObject) { NetworkInfo networkInfo = gameObject.GetComponent <NetworkInfo>(); networkInfo.AttachCubeToLocalPlayer(this, hand); #if DEBUG_AUTHORITY Debug.Log("client " + context.GetClientIndex() + " grabbed cube " + networkInfo.GetCubeId() + " and set ownership sequence to " + networkInfo.GetOwnershipSequence()); #endif // #if DEBUG_AUTHORITY if (!context.IsServer()) { networkInfo.ClearConfirmed(); } else { networkInfo.SetConfirmed(); } for (int i = 0; i < ThrowRingBufferSize; ++i) { hand.throwRingBufferEntries[i].valid = false; hand.throwRingBufferEntries[i].speed = 0.0f; } }
private void SetHandData(HandData handData) { var handState = handData.HandState; var handReflection = GetHandReflection(handData.Hand); //if (OVRPlugin.GetHandState(step, (OVRPlugin.Hand)HandType, ref _handState)) //{ handReflection.HandState.Set(handState); //TODO: hand state is probably overwritten handReflection.IsTracked.Set((handState.Status & OVRPlugin.HandStatus.HandTracked) != 0); handReflection.IsSystemGestureInProgress.Set((handState.Status & OVRPlugin.HandStatus.SystemGestureInProgress) != 0); handReflection.IsPointerPoseValid.Set((handState.Status & OVRPlugin.HandStatus.InputStateValid) != 0); handReflection.PointerPose.Get().localPosition = handState.PointerPose.Position.FromFlippedZVector3f(); handReflection.PointerPose.Get().localRotation = handState.PointerPose.Orientation.FromFlippedZQuatf(); handReflection.HandScale.Set(handState.HandScale); handReflection.HandConfidence.Set((OVRHand.TrackingConfidence)handState.HandConfidence); handReflection.IsInitialized.Set(true);; _leftHandReflection.IsInitializedOverride = true; //} //else //{ //_isInitialized = false; //} }
void UpdateHeldObject(ref HandData hand) { if (hand.gripObject) { // track data to improve throw release in ring buffer int index = (hand.throwRingBufferIndex++) % ThrowRingBufferSize; hand.throwRingBufferEntries[index].valid = true; Vector3 difference = hand.gripObject.transform.position - hand.previousGripObjectPosition; hand.throwRingBufferEntries[index].speed = (float)Math.Sqrt(difference.x * difference.x + difference.z * difference.z) * Constants.RenderFrameRate; // track previous positions and rotations for hand and index finger so we can use this to determine linear and angular velocity at time of release hand.previousHandPosition = hand.transform.position; hand.previousHandRotation = hand.transform.rotation; hand.previousGripObjectPosition = hand.gripObject.transform.position; hand.previousGripObjectRotation = hand.gripObject.transform.rotation; // while an object is held set its last interaction frame to the current sim frame. this is used to boost priority for this object when it is thrown. NetworkInfo networkInfo = hand.gripObject.GetComponent <NetworkInfo>(); networkInfo.SetLastPlayerInteractionFrame((long)context.GetSimulationFrame()); } }
bool DetectGripTransition(ref HandData hand) { if (hand.state == HandState.Grip && hand.gripObject == null) { TransitionToState(ref hand, HandState.Neutral); return(true); } if (hand.input.handTrigger >= GripThreshold) { if (hand.pointObject && hand.pointObjectFrame + PointStickyFrames >= context.GetRenderFrame()) { NetworkInfo networkInfo = hand.pointObject.GetComponent <NetworkInfo>(); if (networkInfo.CanLocalPlayerGrabCube(hand.gripInputStartFrame)) { AttachToHand(ref hand, hand.pointObject); TransitionToState(ref hand, HandState.Grip); return(true); } } } return(false); }
public RealsenseManager(bool isDataOnly) { sm = RS.SenseManager.CreateInstance(); if (!isDataOnly) { rgbReader = RS.SampleReader.Activate(sm); depthReader = RS.SampleReader.Activate(sm); rgbReader.EnableStream(RS.StreamType.STREAM_TYPE_COLOR); depthReader.EnableStream(RS.StreamType.STREAM_TYPE_DEPTH); rgbReader.SampleArrived += RgbReader_SampleArrived; depthReader.SampleArrived += DepthReader_SampleArrived; } // config hand handModule = HandModule.Activate(sm); handConfig = handModule.CreateActiveConfiguration(); handConfig.TrackedJointsEnabled = true; handConfig.StabilizerEnabled = true; handConfig.TrackingMode = TrackingModeType.TRACKING_MODE_FULL_HAND; handConfig.ApplyChanges(); handData = handModule.CreateOutput(); handModule.FrameProcessed += HandModule_FrameProcessed; }
/// <summary> /// Compares two handdatas and tells whether they match. /// </summary> /// <param name="position">The position that should be matched.</param> /// <param name="current">The current data.</param> /// <param name="tollerance">How much each joint is allowed to deviate.</param> /// <returns>Whether the data matches.</returns> public bool AreSimilar(PositionRecord position, HandData current, double tollerance = 0) { tollerance = Math.Pow(tollerance > 0 ? tollerance : TOLLERANCE, 2); // Number of joints. int max = (int)HandData.Joint.MAX; // The total deviation. double total = 0, frac; // The number of non-ignored joints. int nr = 0; for (int i = 0; i < max; i++) { if (position.Ignored[i]) { continue; } // Add square deviation of this joint to the total. frac = position[i].Degrees - current[i].Degrees; total += frac * frac; nr++; } // Check whether this one matches. if (total / nr < tollerance) { return(true); } else { return(false); } }
// Obsolete private double GetChanceOld(string action, int roundNumber, HandData data) { if (action == "R") { return((double)data.Raises[roundNumber] / data.Hands); } if (action == "RE") { return((double)data.ReRaises[roundNumber] / data.Hands); } if (action == "C") { return((double)data.Calls[roundNumber] / data.Hands); } if (action == "Ch") { return((double)data.Checks[roundNumber] / data.Hands); } if (action == "F") { return((double)data.Folds[roundNumber] / data.Hands); } throw new Exception("Unknown action recieved."); }
public void ResetHand(ref HandData hand) { hand.disableReleaseVelocity = true; TransitionToState(ref hand, HandState.Neutral); hand.disableReleaseVelocity = false; hand.touchingObject = false; hand.pointLine = null; hand.pointObject = null; hand.pointObjectFrame = 0; hand.gripObject = null; hand.gripInputStartFrame = 0; hand.gripObjectStartFrame = 0; hand.previousHandPosition = Vector3.zero; hand.previousGripObjectPosition = Vector3.zero; hand.previousHandRotation = Quaternion.identity; hand.previousGripObjectRotation = Quaternion.identity; hand.gripObjectReleaseFrame = 0; hand.gripObjectSupportList = null; }
//public GameObject sort; //private SortCheck sortCheck; //public List<GameObject> handata; // Start is called before the first frame update void Start() { handData = gameData.GetComponent <HandData>(); //sortCheck = gameData.GetComponent<SortCheck>(); move = 1; //SetText(); }
/// <summary> /// Gets updated hand data for the current frame. /// </summary> /// <param name="spatialInteractionSourceState">Platform provided current input source state for the hand.</param> /// <param name="includeMeshData">If set, hand mesh information will be included in <see cref="HandData.Mesh"/>.</param> /// <param name="handData">The output <see cref="HandData"/>.</param> /// <returns>True, if data conversion was a success.</returns> public bool TryGetHandData(SpatialInteractionSourceState spatialInteractionSourceState, bool includeMeshData, out HandData handData) { // Here we check whether the hand is being tracked at all by the WMR system. HandPose handPose = spatialInteractionSourceState.TryGetHandPose(); if (handPose == null) { handData = default; return(false); } // The hand is being tracked, next we verify it meets our confidence requirements to consider // it tracked. var platformJointPoses = new JointPose[jointIndices.Length]; handData = new HandData { TrackingState = handPose.TryGetJoints(spatialCoordinateSystem, jointIndices, platformJointPoses) ? TrackingState.Tracked : TrackingState.NotTracked, UpdatedAt = DateTimeOffset.UtcNow.Ticks }; // If the hand is tracked per requirements, we get updated joint data // and other data needed for updating the hand controller's state. if (handData.TrackingState == TrackingState.Tracked) { handData.RootPose = GetHandRootPose(platformJointPoses); handData.Joints = GetJointPoses(platformJointPoses, handData.RootPose); handData.PointerPose = GetPointerPose(spatialInteractionSourceState); if (includeMeshData && TryGetUpdatedHandMeshData(spatialInteractionSourceState, handPose, out HandMeshData data)) { handData.Mesh = data; } else { // if hand mesh visualization is disabled make sure to destroy our hand mesh observer // if it has already been created. if (handMeshObservers.ContainsKey(spatialInteractionSourceState.Source.Handedness)) { if (spatialInteractionSourceState.Source.Handedness == SpatialInteractionSourceHandedness.Left) { hasRequestedHandMeshObserverLeftHand = false; } else if (spatialInteractionSourceState.Source.Handedness == SpatialInteractionSourceHandedness.Right) { hasRequestedHandMeshObserverRightHand = false; } handMeshObservers.Remove(spatialInteractionSourceState.Source.Handedness); } handData.Mesh = HandMeshData.Empty; } } // Even if the hand is being tracked by the system but the confidence did not // meet our requirements, we return true. This allows the hand controller and visualizers // to react to tracking loss and keep the hand up for a given time before destroying the controller. return(true); }
public void DetachCube(ref HandData d) { var body = d.grip.GetComponent <Rigidbody>(); body.isKinematic = false; d.grip.layer = cubesLayer; ApplyReleaseVelocity(ref d, body, d.hasReleaseVelocity); d.grip.transform.SetParent(null); d.grip = null; if (body.position.y < MinimumCubeHeight) { body.position = body.position.WithY(MinimumCubeHeight); } if (d.supports != null) { WakeUpObjects(d.supports); d.supports = null; } d.point = null; d.pointFrame = 0; d.releaseFrame = context.renderFrame; }
protected virtual void PaintCovexHull(HandData cluster, DrawingContext drawingContext) { if (cluster.ConvexHull.Count > 3) { this.DrawLines(drawingContext, this.whitePen, cluster.ConvexHull.Points.Select(p => new System.Windows.Point(p.X, p.Y)).ToArray()); } }
private void TimeShiftMode(HandData rightHand) { this.Dispatcher.Invoke(new Action(() => { if (this.selectedVideo != null && this.selectedVideo.IsPaused) { this.selectedVideo.Play(); } var rightFinger = rightHand.Fingers.OrderBy(f => f.Location.X).FirstOrDefault(); if (rightFinger != null) { if (!moveMode) { if (this.selectedVideo != null && this.selectedVideo.Duration.HasTimeSpan) { moveMode = true; moveStartTime = this.selectedVideo.Position; moveStart = rightFinger.Location; this.slider.Opacity = 0.8; this.slider.Maximum = this.selectedVideo.Duration.TimeSpan.TotalMilliseconds; this.slider.Value = this.selectedVideo.Position.TotalMilliseconds; this.slider.SetValue(Canvas.LeftProperty, (double)rightFinger.Location.X); this.slider.SetValue(Canvas.TopProperty, (double)rightFinger.Location.Y); } } else { this.CalcTimeSpan(rightFinger.Fingertip); this.selectedVideo.Position = TimeSpan.FromMilliseconds(this.slider.Value); this.slider.SetValue(Canvas.TopProperty, (double)rightFinger.Location.Y); } } })); }
void UpdateState(ref HandData d) { if (d.state == Pointing) { UpdateLine(ref d); ForcePointAnimation(ref d); } else if (d.state == Gripping) { if (IsGripNear(ref d)) { ForceGripAnimation(ref d); } else { ForcePointAnimation(ref d); } if (!d.grip || d.grip.transform.position.y > 0.0f) { return; } var position = d.grip.transform.position; position.y = 0.0f; d.grip.transform.position = position; } }
bool FilterPointObject(ref HandData data, Rigidbody body) { if (!body) { return(false); } var obj = body.gameObject; if (!obj) { return(false); } if (obj.layer != cubesLayer && obj.layer != gripLayer) { return(false); } var cube = obj.GetComponent <NetworkCube>(); if (!cube) { return(false); } return(true); }
bool DetectGrip(ref HandData d) { if (d.state == Gripping && d.grip == null) //when it will execute? { Transition(ref d, Neutral); return(true); } if (d.input.handTrigger < GripThreshold) { return(false); } if (d.point || d.pointFrame + PointStickyFrames < context.renderFrame) { return(false); } var cube = d.point.GetComponent <NetworkCube>(); if (cube.HasHolder() || d.inputFrame <= 0) { return(false); } Grip(ref d, d.point); Transition(ref d, Gripping); return(true); }
void ApplyReleaseVelocity(ref HandData d, Rigidbody r, bool isReleaseVelocityDisabled = false) { if (isReleaseVelocityDisabled) { r.velocity = zero; r.angularVelocity = zero; } else if (IsGripNear(ref d) || IsThrowing(ref d)) { r.velocity = (d.grip.transform.position - d.prevGripPosition) * RenderFrameRate; //throw mode r.angularVelocity = CalculateAngularVelocity(d.prevGripRotation, d.grip.transform.rotation, 1.0f / RenderFrameRate, 0.001f); if (r.velocity.magnitude > MaxThrowSpeed) { r.velocity = (r.velocity / r.velocity.magnitude) * MaxThrowSpeed; } if (r.velocity.x * r.velocity.x + r.velocity.z * r.velocity.z > HardThrowSpeed && r.velocity.y < ThrowVelocityMinY ) { r.velocity = new Vector3(r.velocity.x, ThrowVelocityMinY, r.velocity.z); } } else { r.velocity = 3 * (d.transform.position - d.prevHandPosition) * RenderFrameRate; //placement mode r.angularVelocity = 2 * CalculateAngularVelocity(d.prevHandRotation, d.transform.rotation, 1.0f / RenderFrameRate, 0.1f); } }
void UpdateSnapToHand(ref HandData d) { if (d.input.indexTrigger < IndexThreshold || d.input.indexPressFrame + IndexStickyFrames < context.renderFrame ) { return; } d.input.indexPressFrame = 0; var position = GetHandPosition(ref d); //warp to hand on index grip var direction = d.grip.transform.position - position; if (direction.magnitude > WarpDistance) { var body = d.grip.GetComponent <Rigidbody>(); var cube = d.grip.GetComponent <NetworkCube>(); cube.SmoothMove(position + direction.normalized * WarpDistance, body.rotation); } for (int i = 0; i < ThrowRingBufferSize; ++i) { d.buffer[i].speed = 0.0f; } }
void RotateGrip(ref HandData d) { d.grip.transform.RotateAround( d.grip.transform.position, d.transform.forward, GetStickX(ref d) * RotateSpeed * deltaTime); }
void UpdatePointingLine(ref HandData hand) { if (hand.pointLine) { var lineRenderer = hand.pointLine.GetComponent <LineRenderer>(); Vector3 start = hand.transform.position; Vector3 finish = hand.gripObject.transform.position; if (lineRenderer) { if ((finish - start).magnitude >= 1) { lineRenderer.numPositions = 2; lineRenderer.SetPosition(0, start); lineRenderer.SetPosition(1, finish); lineRenderer.startWidth = LineWidth; lineRenderer.endWidth = LineWidth; } else { lineRenderer.numPositions = 0; } } } }
HandData CreateHand(HandModel model) { HandModel hand_model = Instantiate(model, transform.position, transform.rotation) as HandModel; hand_model.transform.localScale = transform.localScale; HandData hand_data = new HandData(); hand_data.model = hand_model; hand_data.hand_id = -1; return hand_data; }
private void setFinger(int index, HandData data) { //if the finger is not detected, we do not render it if(!data.FingerStatus[index] && _fingers[index].renderer.enabled) { _fingers[index].renderer.enabled = false; } //otherwise, we do render it else if(data.FingerStatus[index] && !_fingers[index].renderer.enabled) { _fingers[index].renderer.enabled = true; } _fingers[index].localPosition = data.FingerTipPositions[index]; }
private void setPalm(HandData data) { //if the palm is detected, we render it if(!_palm.renderer.enabled) _palm.renderer.enabled = true; //set the position of the palm _palm.localPosition = new Vector3(data.PalmPosition.x, data.PalmPosition.y, data.PalmPosition.z); //set the orientation of the palm., using the PalmDirection as the lookat vector, //and the PointingDirection as the up vector _palm.LookAt(_palm.position + data.PalmDirection, data.PointingDirection); //Debug.Log(_palm.eulerAngles.x.ToString("F") + " --- " + _palm.eulerAngles.y.ToString("F") + " --- " + _palm.eulerAngles.z.ToString("F")); // Save Euler Angles of the hand if(HandID == 2) { PlayerPrefs.SetFloat("RX",_palm.eulerAngles.x); PlayerPrefs.SetFloat("RY",_palm.eulerAngles.y); PlayerPrefs.SetFloat("RZ",_palm.eulerAngles.z); } if(HandID == 1) { PlayerPrefs.SetFloat("LX",_palm.eulerAngles.x); PlayerPrefs.SetFloat("LY",_palm.eulerAngles.y); PlayerPrefs.SetFloat("LZ",_palm.eulerAngles.z); } }
public void Update(FrameState state) { wait++; if (wait >= smoothing) { currentFrame += 1; wait = 0; } //if (currentFrame >= recordedMovement.Length - 1) currentFrame = 0; float m = wait / (float)smoothing; if ((value++) % 2 == 0) { currentHand = GetHand(); //currentTransform = Matrix.Lerp(recordedMovement[currentFrame].transform, recordedMovement[currentFrame + 1].transform, m); currentTransform = currentHand.transform; } Matrix headTransform = Camera.instance.rotation; //hand.PhysicsEntity.WorldTransform = Matrix.CreateRotationY(-MathHelper.PiOver2) * currentTransform * headTransform * Matrix.CreateTranslation(Camera.instance.headPosition + headTransform.Forward * 0.2f); hand.PhysicsEntity.WorldTransform = currentTransform * Matrix.CreateRotationY(-MathHelper.PiOver2) * headTransform * Matrix.CreateTranslation(Camera.instance.headPosition + headTransform.Forward * 2 + headTransform.Down * 0.5f); HandleFingers(state); }