// CopySkeleton copies the data from another skeleton. public static void CopySkeleton(ref KinectCommon.NuiSkeletonData source, ref KinectCommon.NuiSkeletonData destination) { // if (null == source) // { // return; // } // // if (null == destination) // { // destination = new Skeleton(); // } destination.eTrackingState = source.eTrackingState; destination.TrackingId = source.TrackingId; destination.Position = source.Position; int jointsCount = (int)KinectCommon.NuiSkeletonPositionIndex.Count; if (destination.SkeletonPositions == null) { destination.SkeletonPositions = new Vector4[jointsCount]; } if (destination.SkeletonTrackingState == null) { destination.SkeletonTrackingState = new KinectCommon.NuiSkeletonPositionTrackingState[jointsCount]; } for (int jointIndex = 0; jointIndex < jointsCount; jointIndex++) { destination.SkeletonPositions[jointIndex] = source.SkeletonPositions[jointIndex]; destination.SkeletonTrackingState[jointIndex] = source.SkeletonTrackingState[jointIndex]; } }
// ConstrainSelfIntersection collides joints with the skeleton to keep the skeleton's hands and wrists from puncturing its body // A cylinder is created to represent the torso. Intersecting joints have their positions changed to push them outside the torso. public void Constrain(ref KinectCommon.NuiSkeletonData skeleton) { // if (null == skeleton) // { // return; // } int SpineShoulderIndex = (int)KinectCommon.NuiSkeletonPositionIndex.SpineShoulder; int SpineBaseIndex = (int)KinectCommon.NuiSkeletonPositionIndex.SpineBase; if (skeleton.SkeletonTrackingState[SpineShoulderIndex] != KinectCommon.NuiSkeletonPositionTrackingState.NotTracked && skeleton.SkeletonTrackingState[SpineBaseIndex] != KinectCommon.NuiSkeletonPositionTrackingState.NotTracked) { Vector3 shoulderDiffLeft = KinectHelper.VectorBetween(ref skeleton, SpineShoulderIndex, (int)KinectCommon.NuiSkeletonPositionIndex.ShoulderLeft); Vector3 shoulderDiffRight = KinectHelper.VectorBetween(ref skeleton, SpineShoulderIndex, (int)KinectCommon.NuiSkeletonPositionIndex.ShoulderRight); float shoulderLengthLeft = shoulderDiffLeft.magnitude; float shoulderLengthRight = shoulderDiffRight.magnitude; // The distance between shoulders is averaged for the radius float cylinderRadius = (shoulderLengthLeft + shoulderLengthRight) * 0.5f; // Calculate the shoulder center and the hip center. Extend them up and down respectively. Vector3 SpineShoulder = (Vector3)skeleton.SkeletonPositions[SpineShoulderIndex]; Vector3 SpineBase = (Vector3)skeleton.SkeletonPositions[SpineBaseIndex]; Vector3 hipShoulder = SpineBase - SpineShoulder; hipShoulder.Normalize(); SpineShoulder = SpineShoulder - (hipShoulder * (ShoulderExtend * cylinderRadius)); SpineBase = SpineBase + (hipShoulder * (HipExtend * cylinderRadius)); // Optionally increase radius to account for bulky avatars cylinderRadius *= RadiusMultiplier; // joints to collide int[] collisionIndices = { (int)KinectCommon.NuiSkeletonPositionIndex.WristLeft, (int)KinectCommon.NuiSkeletonPositionIndex.HandLeft, (int)KinectCommon.NuiSkeletonPositionIndex.WristRight, (int)KinectCommon.NuiSkeletonPositionIndex.HandRight }; foreach (int j in collisionIndices) { Vector3 collisionJoint = (Vector3)skeleton.SkeletonPositions[j]; Vector4 distanceNormal = KinectHelper.DistanceToLineSegment(SpineShoulder, SpineBase, collisionJoint); Vector3 normal = new Vector3(distanceNormal.x, distanceNormal.y, distanceNormal.z); // if distance is within the cylinder then push the joint out and away from the cylinder if (distanceNormal.w < cylinderRadius) { collisionJoint += normal * ((cylinderRadius - distanceNormal.w) * CollisionTolerance); skeleton.SkeletonPositions[j] = (Vector4)collisionJoint; } } } }
// IsTrackedOrInferred checks whether the skeleton joint is tracked or inferred. public static bool IsTrackedOrInferred(KinectCommon.NuiSkeletonData skeleton, int jointIndex) { // if (null == skeleton) // { // return false; // } return(skeleton.SkeletonTrackingState[jointIndex] != KinectCommon.NuiSkeletonPositionTrackingState.NotTracked); }
/// VectorBetween calculates the Vector3 from start to end == subtract start from end public static Vector3 VectorBetween(ref KinectCommon.NuiSkeletonData skeleton, int startJoint, int endJoint) { // if (null == skeleton) // { // return Vector3.Zero; // } Vector3 startPosition = (Vector3)skeleton.SkeletonPositions[startJoint]; Vector3 endPosition = (Vector3)skeleton.SkeletonPositions[endJoint]; return(endPosition - startPosition); }
// Update the filter with a new frame of data and smooth. public void UpdateFilter(ref KinectCommon.NuiSkeletonData skeleton) { // if (null == skeleton) // { // return; // } if (skeleton.eTrackingState != KinectCommon.NuiSkeletonTrackingState.SkeletonTracked) { return; } if (this.init == false) { this.Init(); // initialize with default parameters } //Array jointTypeValues = Enum.GetValues(typeof(KinectCommon.NuiSkeletonPositionIndex)); KinectCommon.NuiTransformSmoothParameters tempSmoothingParams = new KinectCommon.NuiTransformSmoothParameters(); // Check for divide by zero. Use an epsilon of a 10th of a millimeter this.smoothParameters.fJitterRadius = Math.Max(0.0001f, this.smoothParameters.fJitterRadius); tempSmoothingParams.fSmoothing = smoothParameters.fSmoothing; tempSmoothingParams.fCorrection = smoothParameters.fCorrection; tempSmoothingParams.fPrediction = smoothParameters.fPrediction; int jointsCount = (int)KinectCommon.NuiSkeletonPositionIndex.Count; for (int jointIndex = 0; jointIndex < jointsCount; jointIndex++) { //KinectCommon.NuiSkeletonPositionIndex jt = (KinectCommon.NuiSkeletonPositionIndex)jointTypeValues.GetValue(jointIndex); // If not tracked, we smooth a bit more by using a bigger jitter radius // Always filter feet highly as they are so noisy if (skeleton.SkeletonTrackingState[jointIndex] != KinectCommon.NuiSkeletonPositionTrackingState.Tracked) { tempSmoothingParams.fJitterRadius = smoothParameters.fJitterRadius * 2.0f; tempSmoothingParams.fMaxDeviationRadius = smoothParameters.fMaxDeviationRadius * 2.0f; } else { tempSmoothingParams.fJitterRadius = smoothParameters.fJitterRadius; tempSmoothingParams.fMaxDeviationRadius = smoothParameters.fMaxDeviationRadius; } FilterJoint(ref skeleton, jointIndex, ref tempSmoothingParams); } }
// LerpAndApply performs a Lerp and applies the Lerped vector to the skeleton joint. public static void LerpAndApply(ref KinectCommon.NuiSkeletonData skeleton, int jointIndex, Vector3 newJointPos, float lerpValue, KinectCommon.NuiSkeletonPositionTrackingState finalTrackingState) { // if (null == skeleton) // { // return; // } Vector3 jointPos = (Vector3)skeleton.SkeletonPositions[jointIndex]; jointPos = Vector3.Lerp(jointPos, newJointPos, lerpValue); skeleton.SkeletonPositions[jointIndex] = (Vector4)jointPos; skeleton.SkeletonTrackingState[jointIndex] = finalTrackingState; }
// Initializes a new instance of the class. public ClippedLegsFilter() { this.lerpLeftKnee = new TimedLerp(); this.lerpLeftAnkle = new TimedLerp(); this.lerpLeftFoot = new TimedLerp(); this.lerpRightKnee = new TimedLerp(); this.lerpRightAnkle = new TimedLerp(); this.lerpRightFoot = new TimedLerp(); this.filterJoints = new JointPositionsFilter(); this.filteredSkeleton = new KinectCommon.NuiSkeletonData(); // knee, ankle, foot blend amounts this.allTracked = new Vector3(0.0f, 0.0f, 0.0f); // All joints are tracked this.footInferred = new Vector3(0.0f, 0.0f, 1.0f); // foot is inferred this.ankleInferred = new Vector3(0.5f, 1.0f, 1.0f); // ankle is inferred this.kneeInferred = new Vector3(1.0f, 1.0f, 1.0f); // knee is inferred Reset(); }
// Update the filter with a new frame of data and smooth. public void UpdateFilter(ref KinectCommon.NuiSkeletonData skeleton) { if (skeleton.eTrackingState != KinectCommon.NuiSkeletonTrackingState.SkeletonTracked) { return; } if (this.init == false) { this.Init(); // initialize with default parameters } // Check for divide by zero. Use an epsilon of a 10th of a millimeter smoothParameters.fJitterRadius = Math.Max(0.0001f, smoothParameters.fJitterRadius); int jointsCount = (int)KinectCommon.NuiSkeletonPositionIndex.Count; for (int jointIndex = 0; jointIndex < jointsCount; jointIndex++) { FilterJoint(ref skeleton, jointIndex, ref smoothParameters); } }
// Update the filter for one joint. protected void FilterJoint(ref KinectCommon.NuiSkeletonData skeleton, int jointIndex, ref KinectCommon.NuiTransformSmoothParameters smoothingParameters) { float filteredState; float trend; float diffVal; float rawState = (float)skeleton.SkeletonTrackingState[jointIndex]; float prevFilteredState = history[jointIndex].FilteredState; float prevTrend = history[jointIndex].Trend; float prevRawState = history[jointIndex].RawState; // If joint is invalid, reset the filter if (rawState == 0f) { history[jointIndex].FrameCount = 0; } // Initial start values if (history[jointIndex].FrameCount == 0) { filteredState = rawState; trend = 0f; } else if (this.history[jointIndex].FrameCount == 1) { filteredState = (rawState + prevRawState) * 0.5f; diffVal = filteredState - prevFilteredState; trend = (diffVal * smoothingParameters.fCorrection) + (prevTrend * (1.0f - smoothingParameters.fCorrection)); } else { // // First apply jitter filter // diffVal = rawState - prevFilteredState; // // if (diffVal <= smoothingParameters.fJitterRadius) // { // filteredState = (rawState * (diffVal / smoothingParameters.fJitterRadius)) + (prevFilteredState * (1.0f - (diffVal / smoothingParameters.fJitterRadius))); // } // else // { // filteredState = rawState; // } filteredState = rawState; // Now the double exponential smoothing filter filteredState = (filteredState * (1.0f - smoothingParameters.fSmoothing)) + ((prevFilteredState + prevTrend) * smoothingParameters.fSmoothing); diffVal = filteredState - prevFilteredState; trend = (diffVal * smoothingParameters.fCorrection) + (prevTrend * (1.0f - smoothingParameters.fCorrection)); } // Predict into the future to reduce latency float predictedState = filteredState + (trend * smoothingParameters.fPrediction); // Check that we are not too far away from raw data diffVal = predictedState - rawState; if (diffVal > smoothingParameters.fMaxDeviationRadius) { predictedState = (predictedState * (smoothingParameters.fMaxDeviationRadius / diffVal)) + (rawState * (1.0f - (smoothingParameters.fMaxDeviationRadius / diffVal))); } // Save the data from this frame history[jointIndex].RawState = rawState; history[jointIndex].FilteredState = filteredState; history[jointIndex].Trend = trend; history[jointIndex].FrameCount++; // Set the filtered data back into the joint skeleton.SkeletonTrackingState[jointIndex] = (KinectCommon.NuiSkeletonPositionTrackingState)(predictedState + 0.5f); }
// Update the filter for one joint. protected void FilterJoint(ref KinectCommon.NuiSkeletonData skeleton, int jointIndex, ref KinectCommon.NuiTransformSmoothParameters smoothingParameters) { // if (null == skeleton) // { // return; // } //int jointIndex = (int)jt; Vector3 filteredPosition; Vector3 diffvec; Vector3 trend; float diffVal; Vector3 rawPosition = (Vector3)skeleton.SkeletonPositions[jointIndex]; Vector3 prevFilteredPosition = this.history[jointIndex].FilteredPosition; Vector3 prevTrend = this.history[jointIndex].Trend; Vector3 prevRawPosition = this.history[jointIndex].RawPosition; bool jointIsValid = KinectHelper.JointPositionIsValid(rawPosition); // If joint is invalid, reset the filter if (!jointIsValid) { history[jointIndex].FrameCount = 0; } // Initial start values if (this.history[jointIndex].FrameCount == 0) { filteredPosition = rawPosition; trend = Vector3.zero; } else if (this.history[jointIndex].FrameCount == 1) { filteredPosition = (rawPosition + prevRawPosition) * 0.5f; diffvec = filteredPosition - prevFilteredPosition; trend = (diffvec * smoothingParameters.fCorrection) + (prevTrend * (1.0f - smoothingParameters.fCorrection)); } else { // First apply jitter filter diffvec = rawPosition - prevFilteredPosition; diffVal = Math.Abs(diffvec.magnitude); if (diffVal <= smoothingParameters.fJitterRadius) { filteredPosition = (rawPosition * (diffVal / smoothingParameters.fJitterRadius)) + (prevFilteredPosition * (1.0f - (diffVal / smoothingParameters.fJitterRadius))); } else { filteredPosition = rawPosition; } // Now the double exponential smoothing filter filteredPosition = (filteredPosition * (1.0f - smoothingParameters.fSmoothing)) + ((prevFilteredPosition + prevTrend) * smoothingParameters.fSmoothing); diffvec = filteredPosition - prevFilteredPosition; trend = (diffvec * smoothingParameters.fCorrection) + (prevTrend * (1.0f - smoothingParameters.fCorrection)); } // Predict into the future to reduce latency Vector3 predictedPosition = filteredPosition + (trend * smoothingParameters.fPrediction); // Check that we are not too far away from raw data diffvec = predictedPosition - rawPosition; diffVal = Mathf.Abs(diffvec.magnitude); if (diffVal > smoothingParameters.fMaxDeviationRadius) { predictedPosition = (predictedPosition * (smoothingParameters.fMaxDeviationRadius / diffVal)) + (rawPosition * (1.0f - (smoothingParameters.fMaxDeviationRadius / diffVal))); } // Save the data from this frame history[jointIndex].RawPosition = rawPosition; history[jointIndex].FilteredPosition = filteredPosition; history[jointIndex].Trend = trend; history[jointIndex].FrameCount++; // Set the filtered data back into the joint // Joint j = skeleton.Joints[jt]; // j.Position = KinectHelper.Vector3ToSkeletonPoint(predictedPosition); // skeleton.Joints[jt] = j; skeleton.SkeletonPositions[jointIndex] = (Vector4)predictedPosition; }
// Update the filter for one joint. protected void FilterJoint(ref KinectCommon.NuiSkeletonData skeleton, int jointIndex, ref KinectCommon.NuiTransformSmoothParameters smoothingParameters, ref Matrix4x4[] jointOrientations) { // if (null == skeleton) // { // return; // } // int jointIndex = (int)jt; Quaternion filteredOrientation; Quaternion trend; Vector3 fwdVector = (Vector3)jointOrientations[jointIndex].GetColumn(2); if (fwdVector == Vector3.zero) { return; } Quaternion rawOrientation = Quaternion.LookRotation(fwdVector, jointOrientations[jointIndex].GetColumn(1)); Quaternion prevFilteredOrientation = this.history[jointIndex].FilteredBoneOrientation; Quaternion prevTrend = this.history[jointIndex].Trend; Vector3 rawPosition = (Vector3)skeleton.SkeletonPositions[jointIndex]; bool orientationIsValid = KinectHelper.JointPositionIsValid(rawPosition) && KinectHelper.IsTrackedOrInferred(skeleton, jointIndex) && KinectHelper.BoneOrientationIsValid(rawOrientation); if (!orientationIsValid) { if (this.history[jointIndex].FrameCount > 0) { rawOrientation = history[jointIndex].FilteredBoneOrientation; history[jointIndex].FrameCount = 0; } } // Initial start values or reset values if (this.history[jointIndex].FrameCount == 0) { // Use raw position and zero trend for first value filteredOrientation = rawOrientation; trend = Quaternion.identity; } else if (this.history[jointIndex].FrameCount == 1) { // Use average of two positions and calculate proper trend for end value Quaternion prevRawOrientation = this.history[jointIndex].RawBoneOrientation; filteredOrientation = KinectHelper.EnhancedQuaternionSlerp(prevRawOrientation, rawOrientation, 0.5f); Quaternion diffStarted = KinectHelper.RotationBetweenQuaternions(filteredOrientation, prevFilteredOrientation); trend = KinectHelper.EnhancedQuaternionSlerp(prevTrend, diffStarted, smoothingParameters.fCorrection); } else { // First apply a jitter filter Quaternion diffJitter = KinectHelper.RotationBetweenQuaternions(rawOrientation, prevFilteredOrientation); float diffValJitter = (float)Math.Abs(KinectHelper.QuaternionAngle(diffJitter)); if (diffValJitter <= smoothingParameters.fJitterRadius) { filteredOrientation = KinectHelper.EnhancedQuaternionSlerp(prevFilteredOrientation, rawOrientation, diffValJitter / smoothingParameters.fJitterRadius); } else { filteredOrientation = rawOrientation; } // Now the double exponential smoothing filter filteredOrientation = KinectHelper.EnhancedQuaternionSlerp(filteredOrientation, prevFilteredOrientation * prevTrend, smoothingParameters.fSmoothing); diffJitter = KinectHelper.RotationBetweenQuaternions(filteredOrientation, prevFilteredOrientation); trend = KinectHelper.EnhancedQuaternionSlerp(prevTrend, diffJitter, smoothingParameters.fCorrection); } // Use the trend and predict into the future to reduce latency Quaternion predictedOrientation = filteredOrientation * KinectHelper.EnhancedQuaternionSlerp(Quaternion.identity, trend, smoothingParameters.fPrediction); // Check that we are not too far away from raw data Quaternion diff = KinectHelper.RotationBetweenQuaternions(predictedOrientation, filteredOrientation); float diffVal = (float)Math.Abs(KinectHelper.QuaternionAngle(diff)); if (diffVal > smoothingParameters.fMaxDeviationRadius) { predictedOrientation = KinectHelper.EnhancedQuaternionSlerp(filteredOrientation, predictedOrientation, smoothingParameters.fMaxDeviationRadius / diffVal); } // predictedOrientation.Normalize(); // filteredOrientation.Normalize(); // trend.Normalize(); // Save the data from this frame history[jointIndex].RawBoneOrientation = rawOrientation; history[jointIndex].FilteredBoneOrientation = filteredOrientation; history[jointIndex].Trend = trend; history[jointIndex].FrameCount++; // Set the filtered and predicted data back into the bone orientation if (KinectHelper.BoneOrientationIsValid(predictedOrientation)) { jointOrientations[jointIndex].SetTRS(Vector3.zero, predictedOrientation, Vector3.one); } }
// Implements the per-frame filter logic for the arms up patch. public bool FilterSkeleton(ref KinectCommon.NuiSkeletonData skeleton, float deltaNuiTime) { // if (null == skeleton) // { // return false; // } // exit early if we lose tracking on the entire skeleton if (skeleton.eTrackingState != KinectCommon.NuiSkeletonTrackingState.SkeletonTracked) { filterJoints.Reset(); } KinectHelper.CopySkeleton(ref skeleton, ref filteredSkeleton); filterJoints.UpdateFilter(ref filteredSkeleton); // Update lerp state with the current delta NUI time. this.lerpLeftKnee.Tick(deltaNuiTime); this.lerpLeftAnkle.Tick(deltaNuiTime); this.lerpLeftFoot.Tick(deltaNuiTime); this.lerpRightKnee.Tick(deltaNuiTime); this.lerpRightAnkle.Tick(deltaNuiTime); this.lerpRightFoot.Tick(deltaNuiTime); // Exit early if we do not have a valid body basis - too much of the skeleton is invalid. if ((!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.SpineBase)) || (!KinectHelper.IsTrackedOrInferred(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.HipLeft)) || (!KinectHelper.IsTrackedOrInferred(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.HipRight))) { return(false); } // Determine if the skeleton is clipped by the bottom of the FOV. bool clippedBottom = (skeleton.ClippedEdges & KinectCommon.FrameEdges.Bottom) != 0; // Select a mask for the left leg depending on which joints are not tracked. // These masks define how much of the filtered joint positions should be blended // with the raw positions. Based on the tracking state of the leg joints, we apply // more filtered data as more joints lose tracking. Vector3 leftLegMask = this.allTracked; if (!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.KneeLeft)) { leftLegMask = this.kneeInferred; } else if (!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.AnkleLeft)) { leftLegMask = this.ankleInferred; } else if (!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.FootLeft)) { leftLegMask = this.footInferred; } // Select a mask for the right leg depending on which joints are not tracked. Vector3 rightLegMask = this.allTracked; if (!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.KneeRight)) { rightLegMask = this.kneeInferred; } else if (!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.AnkleRight)) { rightLegMask = this.ankleInferred; } else if (!KinectHelper.IsTracked(skeleton, (int)KinectCommon.NuiSkeletonPositionIndex.FootRight)) { rightLegMask = this.footInferred; } // If the skeleton is not clipped by the bottom of the FOV, cut the filtered data // blend in half. float clipMask = clippedBottom ? 1.0f : 0.5f; // Apply the mask values to the joints of each leg, by placing the mask values into the lerp targets. this.lerpLeftKnee.SetEnabled(leftLegMask.x * clipMask); this.lerpLeftAnkle.SetEnabled(leftLegMask.y * clipMask); this.lerpLeftFoot.SetEnabled(leftLegMask.z * clipMask); this.lerpRightKnee.SetEnabled(rightLegMask.x * clipMask); this.lerpRightAnkle.SetEnabled(rightLegMask.y * clipMask); this.lerpRightFoot.SetEnabled(rightLegMask.z * clipMask); // The bSkeletonUpdated flag tracks whether we have modified the output skeleton or not. bool skeletonUpdated = false; // Apply lerp to the left knee, which will blend the raw joint position with the filtered joint position based on the current lerp value. if (this.lerpLeftKnee.IsLerpEnabled()) { int jointIndex = (int)KinectCommon.NuiSkeletonPositionIndex.KneeLeft; KinectHelper.LerpAndApply(ref skeleton, jointIndex, (Vector3)filteredSkeleton.SkeletonPositions[jointIndex], lerpLeftKnee.SmoothValue, KinectCommon.NuiSkeletonPositionTrackingState.Tracked); skeletonUpdated = true; } // Apply lerp to the left ankle. if (this.lerpLeftAnkle.IsLerpEnabled()) { int jointIndex = (int)KinectCommon.NuiSkeletonPositionIndex.AnkleLeft; KinectHelper.LerpAndApply(ref skeleton, jointIndex, (Vector3)filteredSkeleton.SkeletonPositions[jointIndex], lerpLeftAnkle.SmoothValue, KinectCommon.NuiSkeletonPositionTrackingState.Tracked); skeletonUpdated = true; } // Apply lerp to the left foot. if (this.lerpLeftFoot.IsLerpEnabled()) { int jointIndex = (int)KinectCommon.NuiSkeletonPositionIndex.FootLeft; KinectHelper.LerpAndApply(ref skeleton, jointIndex, (Vector3)filteredSkeleton.SkeletonPositions[jointIndex], lerpLeftFoot.SmoothValue, KinectCommon.NuiSkeletonPositionTrackingState.Inferred); skeletonUpdated = true; } // Apply lerp to the right knee. if (this.lerpRightKnee.IsLerpEnabled()) { int jointIndex = (int)KinectCommon.NuiSkeletonPositionIndex.KneeRight; KinectHelper.LerpAndApply(ref skeleton, jointIndex, (Vector3)filteredSkeleton.SkeletonPositions[jointIndex], lerpRightKnee.SmoothValue, KinectCommon.NuiSkeletonPositionTrackingState.Tracked); skeletonUpdated = true; } // Apply lerp to the right ankle. if (this.lerpRightAnkle.IsLerpEnabled()) { int jointIndex = (int)KinectCommon.NuiSkeletonPositionIndex.AnkleRight; KinectHelper.LerpAndApply(ref skeleton, jointIndex, (Vector3)filteredSkeleton.SkeletonPositions[jointIndex], lerpRightAnkle.SmoothValue, KinectCommon.NuiSkeletonPositionTrackingState.Tracked); skeletonUpdated = true; } // Apply lerp to the right foot. if (this.lerpRightFoot.IsLerpEnabled()) { int jointIndex = (int)KinectCommon.NuiSkeletonPositionIndex.FootRight; KinectHelper.LerpAndApply(ref skeleton, jointIndex, (Vector3)filteredSkeleton.SkeletonPositions[jointIndex], lerpRightFoot.SmoothValue, KinectCommon.NuiSkeletonPositionTrackingState.Inferred); skeletonUpdated = true; } return(skeletonUpdated); }