Esempio n. 1
0
    // 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;
                }
            }
        }
    }
Esempio n. 3
0
    // 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);
    }
Esempio n. 4
0
    /// 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);
    }
Esempio n. 5
0
    // 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);
        }
    }
Esempio n. 6
0
    // 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;
    }
Esempio n. 7
0
    // 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);
    }
Esempio n. 10
0
    // 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;
    }
Esempio n. 11
0
    // 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);
        }
    }
Esempio n. 12
0
    // 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);
    }