public override void ControllerUpdate() { base.ControllerUpdate(); if (!model.inputDataProvider) { return; } if (!gameObject.activeSelf) { return; } masterReprKey = AvatarModel.key; model.inputDataProvider.UpdateData(); // On hand tracked if (conf.ignoreTrackingLoss || model.inputDataProvider.confidence > conf.handTrackingLostUnderConfidence) { if (!model.handIsTracked) { model.handIsTracked = true; model.view.onHandTrackingRecovered.Invoke(); } if (model.isPredicting) { model.isPredicting = false; model.view.onPredictionInterrupted.Invoke(); } // If recovered from tracking loss if (timeSinceLastValidRecord != 0.0f) { timeSinceLastValidRecord = 0.0f; if (conf.hideMasterWhenLost) { model.hand.specificView.SetHandVisuals(true, masterReprKey); } if (conf.hideSlaveWhenLost) { model.hand.specificView.SetHandVisuals(true, PuppetModel.key); model.hand.specificView.SetHandPhysics(true); } } // Calculate clamping if (conf.useHandClamping) { if (conf.gradualHandClamping) { handClampLerp = Mathf.Lerp(0.0f, conf.startDecreasingHandClampUnderConfidence, model.inputDataProvider.confidence); } else if (model.inputDataProvider.confidence > conf.startDecreasingHandClampUnderConfidence) { handClampLerp = 1.0f; // High confidece. Clamp set to highest } else { handClampLerp = 0.0f; // Low confidence. Clamp set to lowest } maxHandLinearSpeed = Mathf.Lerp(conf.lowestHandLinearSpeed, model.highestLinearSpeed, handClampLerp); maxHandAngularSpeed = Mathf.Lerp(conf.lowestHandAngularSpeed, model.highestAngularSpeed, handClampLerp); } else { maxHandLinearSpeed = -1.0f; maxHandAngularSpeed = -1.0f; } // Update record if (model.configuration.recordTracking) { InputHelpers.RecordBone(model.boneRecords, model.inputDataProvider.bones[0], 0); // RecordBone(model.inputDataProvider.bones[1], 1); } // Noise reduction if (model.configuration.movingAverage != MovingAverage.None) { model.inputDataProvider.bones[0] = ReduceNoise(model.boneRecords[0], 0); // model.inputDataProvider.bones[1] = ReduceNoise(model.boneRecords[1], 1); } // Use or get what to move as wrist if (!model.moveThisAsWrist) { model.moveThisAsWrist = model.rigMap.wrist.master.transformRef; } // Find a point of reference (if neede) if (model.replicatedTsf && !model.referenceTsf) { model.referenceTsf = HPTK.core.trackingSpace; } // Update pos and rot for wrist and forearm if (conf.updateWrist && model.bonesToUpdate[0] != null) { // Update wrist position and rotation UpdateBoneTsfPos(model.rigMap.wrist.master, model.moveThisAsWrist, model.inputDataProvider.bones[0], maxHandLinearSpeed, model.referenceTsf, model.replicatedTsf); UpdateBoneTsfRot(model.rigMap.wrist.master, model.moveThisAsWrist, model.inputDataProvider.bones[0], maxHandAngularSpeed, model.referenceTsf, model.replicatedTsf); } if (conf.updateForearm && model.bonesToUpdate[1] != null) { // Optional if (model.configuration.recordTracking) { InputHelpers.RecordBone(model.boneRecords, model.inputDataProvider.bones[1], 1); } // Update wrist position and rotation UpdateBoneTsfPos(model.rigMap.forearm.master, model.rigMap.forearm.master.transformRef, model.inputDataProvider.bones[1], maxHandLinearSpeed, model.referenceTsf, model.replicatedTsf); UpdateBoneTsfRot(model.rigMap.forearm.master, model.rigMap.forearm.master.transformRef, model.inputDataProvider.bones[1], maxHandAngularSpeed, model.referenceTsf, model.replicatedTsf); // Optional } } // On hand tracking loss else { if (model.handIsTracked) { model.handIsTracked = false; model.view.onHandTrackingLost.Invoke(); } if (timeSinceLastValidRecord == 0.0f) { if (conf.usePredictiveTrackingWhenLost) { acceleration = (0.0f - wristSpeed) / conf.maxPredictionTime; predictedDirection = wristVelocityDirection; lastWristPosition = model.rigMap.wrist.transformRef.position; if (!model.isPredicting) { model.isPredicting = true; model.view.onPredictionStart.Invoke(); } } if (conf.hideMasterWhenLost) { model.hand.specificView.SetHandVisuals(false, masterReprKey); } if (conf.hideSlaveWhenLost) { model.hand.specificView.SetHandVisuals(false, PuppetModel.key); model.hand.specificView.SetHandPhysics(false); } } timeSinceLastValidRecord = Time.timeSinceLevelLoad - timeOfLastRecord; if (conf.usePredictiveTrackingWhenLost) { if (timeSinceLastValidRecord < conf.maxPredictionTime) { // Predict new position // currentTime - lastRecordTime = 0 -> velocity = initial // currentTime - lastRecordTime = maxPeedictionTime -> velocity = 0 newDisplacement = wristSpeed * timeSinceLastValidRecord + 0.5f * acceleration * timeSinceLastValidRecord * timeSinceLastValidRecord; predictedDirection = Quaternion.Slerp(wristDirectionChange, Quaternion.identity, timeSinceLastValidRecord / conf.maxPredictionTime) * wristVelocityDirection; model.rigMap.wrist.transformRef.position = lastWristPosition + predictedDirection * newDisplacement; } else { if (model.isPredicting) { model.isPredicting = false; model.view.onPredictionTimeLimitReached.Invoke(); } } } } // On fingers tracked if (conf.ignoreTrackingLoss || model.inputDataProvider.confidence > conf.fingersTrackingLostUnderConfidence) { if (!model.fingersAreTracked) { model.fingersAreTracked = true; model.view.onFingersTrackingRecovered.Invoke(); } // Calculate clamping if (conf.useFingerClamping) { if (conf.gradualFingerClamping) { fingerClampLerp = Mathf.Lerp(0.0f, conf.startDecreasingFingerClampUnderConfidence, model.inputDataProvider.confidence); } else if (model.inputDataProvider.confidence > conf.startDecreasingFingerClampUnderConfidence) { fingerClampLerp = 1.0f; // High confidece. Clamp set to highest } else { fingerClampLerp = 0.0f; // Low confidence. Clamp set to lowest } // maxFingerLinearSpeed = Mathf.Lerp(model.lowestMasterBoneLinearSpeed, model.highestLinearSpeed, fingerClampLerp); maxFingerAngularSpeed = Mathf.Lerp(conf.lowestFingerAngularSpeed, model.highestAngularSpeed, fingerClampLerp); } else { // maxFingerLinearSpeed = -1.0f; maxFingerAngularSpeed = -1.0f; } // Finger bones start at i=2 (i=0 -> wrist, i=1 -> forearm) for (int i = 2; i < model.bonesToUpdate.Length; i++) { // Update record if (model.configuration.recordTracking) { InputHelpers.RecordBone(model.boneRecords, model.inputDataProvider.bones[i], i); } // Noise reduction if (model.configuration.movingAverage != MovingAverage.None) { model.inputDataProvider.bones[i] = ReduceNoise(model.boneRecords[i], i); } // Update only fingers rotation (assuming hierachy) if (model.bonesToUpdate[i] != null) { BoneModel bone = model.bonesToUpdate[i]; AbstractTsf inputData = model.inputDataProvider.bones[i]; // Estimate rotation. Assuming sorted array of bones (i-1 => thumb0, i => thumb1, i+1 => thumb2) if (!model.rigMap.thumb0 && bone == model.rigMap.thumb1) { inputData.rotation = EstimateLocalRotation(model.inputDataProvider.bones[i - 1], model.inputDataProvider.bones[i]); } else if (!model.rigMap.pinky0 && bone == model.rigMap.pinky1) { inputData.rotation = EstimateLocalRotation(model.inputDataProvider.bones[i - 1], model.inputDataProvider.bones[i]); } UpdateBoneTsfRot(bone.master, bone.master.transformRef, inputData, maxFingerAngularSpeed, null, null); } } } // On fingers tracking loss else { if (model.fingersAreTracked) { model.fingersAreTracked = false; model.view.onFingersTrackingLost.Invoke(); } } // If we need to record and confidence is good enough to record if (conf.usePredictiveTrackingWhenLost && model.inputDataProvider.confidence > conf.saveHandHistoricOverConfidence) { // Before updating wristVelocityDirection and wristPosition wristDirectionChange = Quaternion.FromToRotation(wristVelocityDirection, (model.rigMap.wrist.transformRef.position - wristPosition).normalized); // Before updating wristPosition and timeOfLastRecord deltaTime = Time.timeSinceLevelLoad - timeOfLastRecord; displacement = Vector3.Distance(wristPosition, model.rigMap.wrist.transformRef.position); displacement = Mathf.Clamp(displacement, 0.0f, conf.maxPredictionDisplacement * deltaTime); wristSpeed = displacement / deltaTime; wristVelocityDirection = (model.rigMap.wrist.transformRef.position - wristPosition).normalized; // Update wristPosition wristPosition = model.rigMap.wrist.transformRef.position; // Update timeOfLastRecord timeOfLastRecord = Time.timeSinceLevelLoad; } // Hand scaling if (model.updateRealScale && model.inputDataProvider.scale != model.hand.realScale) { model.hand.realScale = model.inputDataProvider.scale; } }