From da4dde44e0bdb296c55c979ada074cff57864ff3 Mon Sep 17 00:00:00 2001
From: pnossol <patrick.nossol@gmail.com>
Date: Tue, 27 Dec 2022 12:03:21 +0100
Subject: [PATCH] updating files and hopefully fixing upload error

---
 Content/SaveSequenceAnimBP.uasset           |   4 +-
 Content/SaveSequenceRig.uasset              |   4 +-
 Content/bein2.uasset                        |   3 -
 Source/MoCapPlugin/Private/MCController.cpp | 208 +++++++++++++++++---
 Source/MoCapPlugin/Private/MCPawn.cpp       |   9 +-
 Source/MoCapPlugin/Public/MCAnimInstance.h  |   5 +
 Source/MoCapPlugin/Public/MCController.h    |  32 ++-
 Source/MoCapPlugin/Public/MCPawn.h          |   2 +-
 8 files changed, 220 insertions(+), 47 deletions(-)
 delete mode 100644 Content/bein2.uasset

diff --git a/Content/SaveSequenceAnimBP.uasset b/Content/SaveSequenceAnimBP.uasset
index e56f5f3..0a155c7 100644
--- a/Content/SaveSequenceAnimBP.uasset
+++ b/Content/SaveSequenceAnimBP.uasset
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c718dc166debab66b55146bf0155fd0c4ccf20613ea71515ccc551651234fbef
-size 220606
+oid sha256:7db37ad8544a1dcb8000c8101b9c0d6ef109ed98c98b37aa0ab2fc7b7ed79856
+size 232162
diff --git a/Content/SaveSequenceRig.uasset b/Content/SaveSequenceRig.uasset
index 3e5c8ee..96f37dd 100644
--- a/Content/SaveSequenceRig.uasset
+++ b/Content/SaveSequenceRig.uasset
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:2ad305b8224b6a04b464537ae7df46b0ee4104dccd0df0448f75eda2dfa10937
-size 4809920
+oid sha256:eaaa62bba28de423b846712d4cad1743c2f26d0799b7f56f723cbef9ffec15b3
+size 4881932
diff --git a/Content/bein2.uasset b/Content/bein2.uasset
deleted file mode 100644
index ed9f8b4..0000000
--- a/Content/bein2.uasset
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:974a53a571ca234c46f45dbe6478d6ceb5ca6c4febfcb90f223e5b93abc1c12d
-size 4894386
diff --git a/Source/MoCapPlugin/Private/MCController.cpp b/Source/MoCapPlugin/Private/MCController.cpp
index 6924685..cd8a20a 100644
--- a/Source/MoCapPlugin/Private/MCController.cpp
+++ b/Source/MoCapPlugin/Private/MCController.cpp
@@ -51,6 +51,10 @@ void AMCController::BeginPlay() {
 
 	Pawn->LoadMeasurementFile(MeasurementPath);
 	Pawn->GetAnimInstance()->PawnOwner = Pawn;
+	if (LeftFootPlane && RightFootPlane) {
+		Pawn->GetAnimInstance()->LeftFootPlane = LeftFootPlane->GetActorTransform();
+		Pawn->GetAnimInstance()->RightFootPlane = RightFootPlane->GetActorTransform();
+	}
 	if (bFingerTrackingEnabled) {
 		USteamVRInputDeviceFunctionLibrary::SetCurlsAndSplaysState(true, true);
 	}
@@ -62,6 +66,8 @@ void AMCController::BeginPlay() {
 
 	LastAddOffsets = AdditionalOffsets;
 
+	InitialPelvisFootDist = FMath::Abs(Pawn->SkeletalMesh->GetSocketLocation("pelvis").Z - Pawn->SkeletalMesh->GetSocketLocation("foot_l").Z);
+	InitialPelvisFootDist = FVector::Dist(Pawn->SkeletalMesh->GetSocketLocation("thigh_l"), Pawn->SkeletalMesh->GetSocketLocation("calf_l")) + FVector::Dist(Pawn->SkeletalMesh->GetSocketLocation("calf_l"), Pawn->SkeletalMesh->GetSocketLocation("foot_l"));
 }
 
 void AMCController::Tick(float DeltaTime) {
@@ -182,14 +188,14 @@ void AMCController::FinishEditAnim() {
 	AnimSaveState = FAnimSaveState();
 }
 
-void AMCController::ScaleAnimDataInterval(int start, int end, float DeltaTime) {
+void AMCController::ScaleAnimDataInterval(int start, int end, float DeltaTime, LinearTransformType LinearTransType, float EasingExponent) {
 	
 	FTimespan StartTime = AnimSaveState.AnimData[start].Timestamp;
 	float OldTime = (AnimSaveState.AnimData[end].Timestamp - AnimSaveState.AnimData[start].Timestamp).GetTotalSeconds();
 	float NewTime = OldTime + DeltaTime;
 	float ScaleFactor = NewTime / OldTime;
 	int OldNrFrames = end + 1 - start;
-	int NewNrFrames = ScaleFactor * OldNrFrames;
+	int NewNrFrames = ScaleFactor * (float)OldNrFrames;
 
 	TArray<FProcessedAnimData> OldAnimData;
 	for (int i = 0; i < AnimSaveState.AnimData.Num(); i++) {
@@ -208,8 +214,14 @@ void AMCController::ScaleAnimDataInterval(int start, int end, float DeltaTime) {
 
 		FProcessedAnimData Data;
 		Data.Timestamp = StartTime + FTimespan::FromSeconds((float)i * NewTime / (float)NewNrFrames);
-		//SmoothStop
-		float TransformedI = 1.f - FMath::Pow(1.f - ((float)i / (float)NewNrFrames), GestureHoldExcessEasingExponent);
+		
+		float TransformedI = i;
+		if (LinearTransType == LinearTransformType::SmoothStop) {
+			TransformedI = 1.f - FMath::Pow(1.f - ((float)i / (float)NewNrFrames), EasingExponent);
+		}
+		else if (LinearTransType == LinearTransformType::SmoothStart) {
+			TransformedI = FMath::Pow((float)i / (float)NewNrFrames, EasingExponent);
+		}
 		FTimespan OldTimestamp = StartTime + FTimespan::FromSeconds(TransformedI * OldTime);
 
 		bool stop = false;
@@ -285,7 +297,7 @@ void AMCController::ScaleAnimDataInterval(int start, int end, float DeltaTime) {
 
 }
 
-bool AMCController::PreprocessRecording(const TArray<float>& HaltingPoints) {
+bool AMCController::PreprocessRecording(float StartHaltingPoint, float EndHaltingPoint) {
 
 	UMCAnimInstance* AI = AnimSaveState.Pawn->GetAnimInstance();
 
@@ -530,9 +542,11 @@ bool AMCController::PreprocessRecording(const TArray<float>& HaltingPoints) {
 		}
 	}
 
-	//---find last slow part of the gesture and scale it up---
-		
-	if (HaltingPoints.Num() > 0) {
+
+	//---find halting points and scale them up---
+	
+	//end point
+	if (EndHaltingPoint > 0.f) {
 
 		FTimespan Start;
 		bool StartTimeInitialized = false;
@@ -574,16 +588,88 @@ bool AMCController::PreprocessRecording(const TArray<float>& HaltingPoints) {
 
 			}
 
-			//or time unit?
-			if (!hold && HaltingPoints[0] <= (AnimData.Timestamp - Start).GetTotalSeconds()) {
+			if (!hold && EndHaltingPoint <= (AnimData.Timestamp - Start).GetTotalSeconds()) {
 				hold = true;
 				holdStartingIndex = i;
 			}
 
 		}
 
-		if (hold && GestureHoldExcessTime > 0.f) {
-			ScaleAnimDataInterval(holdStartingIndex, AnimSaveState.AnimData.Num() - 2, GestureHoldExcessTime);
+		if (hold && GestureHoldExcessEndTime > 0.f) {
+			ScaleAnimDataInterval(holdStartingIndex, AnimSaveState.AnimData.Num() - 2, GestureHoldExcessEndTime, LinearTransformType::SmoothStop, GestureHoldEndExcessEasingExponent);
+		}
+
+	}
+
+	//start point
+	if (StartHaltingPoint >= 0.f) {
+
+		FTimespan Start;
+		bool StartTimeInitialized = false;
+		bool hold = true;
+		int holdEndingIndex = -1;
+		int firstMarkerIndex = -1;
+
+		for (int i = 0; i < AnimSaveState.AnimData.Num(); i++) {
+
+			FProcessedAnimData& AnimData = AnimSaveState.AnimData[i];
+
+			if (AnimData.IsMarker || AnimData.IsEnd) {
+				if (firstMarkerIndex < 0) {
+					firstMarkerIndex = i;
+				}
+				continue;
+			}
+			if (firstMarkerIndex < 0) {
+				continue;
+			}
+
+			if (!StartTimeInitialized) {
+				StartTimeInitialized = true;
+				Start = AnimData.Timestamp;
+			}
+
+			if (hold && StartHaltingPoint <= (AnimData.Timestamp - Start).GetTotalSeconds()) {
+				hold = false;
+				holdEndingIndex = i;
+				break;
+			}
+
+		}
+
+		for (int i = firstMarkerIndex + 1; i < holdEndingIndex + 1; i++) {
+
+			FProcessedAnimData& AnimData = AnimSaveState.AnimData[i];
+
+			if (AnimData.IsMarker || AnimData.IsEnd) {
+				continue;
+			}
+
+			//process sensor data
+
+			FSensorData& SensorData = AnimData.SensorData;
+			for (int j = 0; j < EBodyPart::LAST; j++) {
+
+				EBodyPart Type = EBodyPart(j);
+				FSensorDataEntry* Entry = SensorData.GetEntry(Type);
+
+				if (Type == EBodyPart::HandL || Type == EBodyPart::HandR
+					|| Type == EBodyPart::LowerArmL || Type == EBodyPart::LowerArmR) {
+
+					if (DoHolding) {
+						if (Type == EBodyPart::HandL || Type == EBodyPart::HandR) {
+							Entry->Pos = AnimSaveState.AnimData[holdEndingIndex].SensorData.GetEntry(Type)->Pos;
+						}
+						Entry->Rot = AnimSaveState.AnimData[holdEndingIndex].SensorData.GetEntry(Type)->Rot;
+					}
+
+				}
+			}
+
+		}
+
+		if (!hold && GestureHoldExcessStartTime > 0.f) {
+			ScaleAnimDataInterval(firstMarkerIndex + 1, holdEndingIndex + 1, GestureHoldExcessStartTime, LinearTransformType::SmoothStart, GestureHoldStartExcessEasingExponent);
 		}
 
 	}
@@ -598,7 +684,7 @@ bool AMCController::PreprocessRecording(const TArray<float>& HaltingPoints) {
 		FProcessedAnimData& AnimData = AnimSaveState.AnimData[i];
 
 		if (AnimData.IsMarker || AnimData.IsEnd) {
-			
+
 			FVector cog(0, 0, 0);
 			for (int j = start; j < i; j++) {
 				FSensorData& SensorData = AnimSaveState.AnimData[j].SensorData;
@@ -622,21 +708,78 @@ bool AMCController::PreprocessRecording(const TArray<float>& HaltingPoints) {
 				}
 			}
 
+			FVector LGoalPos = FVector(5, -5, 6) + LeftFootPlane->GetActorLocation();
+			FQuat LGoalRot = FRotator(90, 0, 6).Quaternion() * LeftFootPlane->GetActorQuat();
+			FVector RGoalPos = FVector(-5, -5, 6) + RightFootPlane->GetActorLocation();
+			FQuat RGoalRot = FRotator(-90, 0, 186).Quaternion() * RightFootPlane->GetActorQuat();
+
+			//float maxDist = - 5.0 + FVector::Dist(Pawn->SkeletalMesh->GetSocketLocation("pelvis"), Pawn->SkeletalMesh->GetSocketLocation("foot_l"));
+			float maxDist = InitialPelvisFootDist;
+			//maxDist = Pawn->GetAnimInstance()->Measurements.LowerLeg + Pawn->GetAnimInstance()->Measurements.UpperLeg;
+			float moveDown = 0.f;
 			for (int j = start; j < i; j++) {
 				FSensorData& SensorData = AnimSaveState.AnimData[j].SensorData;
-				FVector LPos = SensorData.GetEntry(EBodyPart::LowerLegL)->Pos;
-				FQuat LRot = SensorData.GetEntry(EBodyPart::LowerLegL)->Rot;
-				Pawn->OffsetSensorData(EBodyPart::LowerLegL, LPos, LRot);
-				FVector RPos = SensorData.GetEntry(EBodyPart::LowerLegR)->Pos;
+
+				FVector BPos = SensorData.GetEntry(EBodyPart::LowerBody)->Pos;
+				FQuat BRot = SensorData.GetEntry(EBodyPart::LowerBody)->Rot;
+				Pawn->OffsetSensorData(EBodyPart::LowerBody, BPos, BRot);
+
+				float dist1 = FVector::Dist(BPos, LGoalPos);
+				float dist2 = FVector::Dist(BPos, RGoalPos);
+				dist1 = FMath::Abs(BPos.Z - LGoalPos.Z);
+				dist2 = FMath::Abs(BPos.Z - RGoalPos.Z);
+				float maxDistTmp = 0.f;
+				if (dist1 > dist2 && std::_Is_finite(dist1)) {
+					maxDistTmp = dist1;
+				}
+				else if (std::_Is_finite(dist2)) {
+					maxDistTmp = dist2;
+				}
+				if (maxDistTmp > maxDist && maxDistTmp - maxDist > moveDown){
+					moveDown = maxDistTmp - maxDist;
+				}
+			}
+
+			if (moveDown > MoveDownCap) {
+				moveDown = MoveDownCap;
+			}
+
+			for (int j = start; j < i; j++) {
+				FSensorData& SensorData = AnimSaveState.AnimData[j].SensorData;
+				for (int k = 0; k < EBodyPart::LAST; k++) {
+					EBodyPart Type = EBodyPart(k);
+					FSensorDataEntry* Entry = SensorData.GetEntry(Type);
+					Entry->Pos += FVector(0, 0, -moveDown);
+				}
+			}
+
+			for (int j = start; j < i; j++) {
+				FSensorData& SensorData = AnimSaveState.AnimData[j].SensorData;
+				//FVector LPos = SensorData.GetEntry(EBodyPart::LowerLegL)->Pos;
+				//FQuat LRot = SensorData.GetEntry(EBodyPart::LowerLegL)->Rot;
+				//Pawn->OffsetSensorData(EBodyPart::LowerLegL, LPos, LRot);
+				/*FVector RPos = SensorData.GetEntry(EBodyPart::LowerLegR)->Pos;
 				FQuat RRot = SensorData.GetEntry(EBodyPart::LowerLegR)->Rot;
-				Pawn->OffsetSensorData(EBodyPart::LowerLegR, RPos, RRot);
+				Pawn->OffsetSensorData(EBodyPart::LowerLegR, RPos, RRot);*/
 
-				shiftAmount = LeftFootPlane->GetActorLocation() - LPos - FVector(-4, 10, 0);
-				SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.X += shiftAmount.X;
-				SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.Y += shiftAmount.Y;
-				shiftAmount = RightFootPlane->GetActorLocation() - RPos - FVector(4, 10, 0);
+				//shiftAmount = LeftFootPlane->GetActorLocation() - LPos - FVector(-4, 10, 0);
+				//SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.X += shiftAmount.X;
+				//SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.Y += shiftAmount.Y;
+				/*shiftAmount = RightFootPlane->GetActorLocation() - RPos - FVector(4, 10, 0);
 				SensorData.GetEntry(EBodyPart::LowerLegR)->Pos.X += shiftAmount.X;
 				SensorData.GetEntry(EBodyPart::LowerLegR)->Pos.Y += shiftAmount.Y;
+
+				FVector LGoal = LeftFootPlane->GetActorLocation() - FVector(-4, 10, 0);
+				FQuat LRot = SensorData.GetEntry(EBodyPart::LowerLegL)->Rot;
+				Pawn->OffsetSensorData(EBodyPart::LowerLegL, LGoal, LRot, true);
+				SensorData.GetEntry(EBodyPart::LowerLegL)->Pos = LGoal;
+				UE_LOG(LogTemp, Warning, TEXT("MIMI %f %f %f"), SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.X, SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.Y, SensorData.GetEntry(EBodyPart::LowerLegL)->Pos.Z);
+				*/
+				SensorData.GetEntry(EBodyPart::LowerLegL)->Pos = LGoalPos;
+				SensorData.GetEntry(EBodyPart::LowerLegL)->Rot = LGoalRot;
+				SensorData.GetEntry(EBodyPart::LowerLegR)->Pos = RGoalPos;
+				SensorData.GetEntry(EBodyPart::LowerLegR)->Rot = RGoalRot;
+
 			}
 
 
@@ -722,17 +865,22 @@ void AMCController::SaveAnimSnapshots() {
 	}
 	const TArray<FSnapshotAnimations>& Anims = AnimSaveState.Pawn->GetAnimInstance()->SnapshotAnimations;
 
+	FString Dest = NameOfRecording;
+	if (SaveToDest != "") {
+		Dest = SaveToDest;
+	}
+
 	FString FolderName;
 	int j = 0;
 	do {
 		if (j == 0) {
-			FolderName = NameOfRecording;
+			FolderName = Dest;
 		}
 		else if (j < 10) {
-			FolderName = NameOfRecording + "_0" + FString::FromInt(j);
+			FolderName = Dest + "_0" + FString::FromInt(j);
 		}
 		else {
-			FolderName = NameOfRecording + "_" + FString::FromInt(j);
+			FolderName = Dest + "_" + FString::FromInt(j);
 		}
 		j++;
 	} while (FPaths::DirectoryExists(FPaths::ProjectContentDir() + "/" + FolderName));
@@ -743,10 +891,10 @@ void AMCController::SaveAnimSnapshots() {
 	for (int i = 0; i < Anims.Num(); i++) {
 		FString AnimName;
 		if (i < 10) {
-			AnimName = NameOfRecording + "_0" + FString::FromInt(i + 1);
+			AnimName = Dest + "_0" + FString::FromInt(i + 1);
 		}
 		else {
-			AnimName = NameOfRecording + "_" + FString::FromInt(i + 1);
+			AnimName = Dest + "_" + FString::FromInt(i + 1);
 		}
 
 		UAnimSequence* AnimSequence = SaveAsAnimSequence(Anims[i], AnimName, FolderName);
@@ -789,7 +937,7 @@ UAnimSequence* AMCController::SaveAsAnimSequence(const FSnapshotAnimations& Reco
 
 			AnimationSequence->SetRawNumberOfFrame(Recording.Snapshots.Num());
 			AnimationSequence->SequenceLength = (Recording.EndTime - Recording.StartTime).GetTotalSeconds();
-
+			
 			SetBonesAnimationInAnimSeq(Recording, AnimationSequence);
 
 			FAssetRegistryModule::AssetCreated(AnimationSequence);
@@ -1129,7 +1277,7 @@ void AMCController::SaveAnimationEditor() {
 	SaveAnimation({}, false);
 }
 
-void AMCController::SaveAnimation(const TArray<float>& HaltingPoints, bool skipTranslation) {
+void AMCController::SaveAnimation(float StartHaltingPoint, float EndHaltingPoint, bool skipTranslation) {
 
 	if (IsSavingToAnim) {
 		return;
@@ -1224,7 +1372,7 @@ void AMCController::SaveAnimation(const TArray<float>& HaltingPoints, bool skipT
 
 	AnimSaveState.Pawn->GetAnimInstance()->AdditionalOffsets = AdditionalOffsets;
 
-	bool isValid = PreprocessRecording(HaltingPoints);
+	bool isValid = PreprocessRecording(StartHaltingPoint, EndHaltingPoint);
 	if (!isValid) {
 		IsSavingToAnim = false;
 		return;
diff --git a/Source/MoCapPlugin/Private/MCPawn.cpp b/Source/MoCapPlugin/Private/MCPawn.cpp
index a8317be..ba69d77 100644
--- a/Source/MoCapPlugin/Private/MCPawn.cpp
+++ b/Source/MoCapPlugin/Private/MCPawn.cpp
@@ -529,7 +529,7 @@ void AMCPawn::InputFingerDataToAnimInstance(TSharedPtr<FJsonObject> Data) {
 
 }
 
-void AMCPawn::OffsetSensorData(EBodyPart part, FVector & Pos, FQuat & Rot) {
+void AMCPawn::OffsetSensorData(EBodyPart part, FVector & Pos, FQuat & Rot, bool inverse) {
 
 	FSensorOffset* Offset = nullptr;
 	UMCAnimInstance* AI = GetAnimInstance();
@@ -566,8 +566,13 @@ void AMCPawn::OffsetSensorData(EBodyPart part, FVector & Pos, FQuat & Rot) {
 		break;
 	}
 
+	FTransform OffsetTrans = Offset->DeltaTransform;
+	if (inverse) {
+		OffsetTrans = OffsetTrans.Inverse();
+	}
+
 	FTransform Transform(Rot, Pos);
-	Transform = Offset->DeltaTransform * Transform;
+	Transform = OffsetTrans * Transform;
 
 	Rot = Transform.GetRotation();
 	Pos = Transform.GetTranslation();
diff --git a/Source/MoCapPlugin/Public/MCAnimInstance.h b/Source/MoCapPlugin/Public/MCAnimInstance.h
index bb16e0e..4c9eafd 100644
--- a/Source/MoCapPlugin/Public/MCAnimInstance.h
+++ b/Source/MoCapPlugin/Public/MCAnimInstance.h
@@ -63,6 +63,11 @@ public:
 	bool SkipCalibration;
 
 	UPROPERTY(BlueprintReadWrite)
+	FTransform LeftFootPlane;
+
+	UPROPERTY(BlueprintReadWrite)
+	FTransform RightFootPlane;
+
 	FVector Scale;
 
 	virtual void NativeInitializeAnimation() override;
diff --git a/Source/MoCapPlugin/Public/MCController.h b/Source/MoCapPlugin/Public/MCController.h
index 32d2a25..71e0482 100644
--- a/Source/MoCapPlugin/Public/MCController.h
+++ b/Source/MoCapPlugin/Public/MCController.h
@@ -24,6 +24,11 @@ struct FProcessedAnimData {
 	FSensorData SensorData;
 };
 
+enum LinearTransformType {
+	SmoothStart,
+	SmoothStop
+};
+
 USTRUCT()
 struct FAnimSaveState {
 	GENERATED_BODY()
@@ -96,11 +101,11 @@ protected:
 	void RecordMode();
 	void SaveToAnimMode();
 
-	void ScaleAnimDataInterval(int start, int end, float DeltaTime);
-	bool PreprocessRecording(const TArray<float>& HaltingPoints);
+	void ScaleAnimDataInterval(int start, int end, float DeltaTime, LinearTransformType LinearTransType, float EasingExponent);
+	bool PreprocessRecording(float StartHaltingPoint, float EndHaltingPoint);
 	void InputNextFrame();
 
-	void SaveAnimation(const TArray<float>& HaltingPoints, bool skipTranslation = false);
+	void SaveAnimation(float StartHaltingPoint, float EndHaltingPoint, bool skipTranslation = false);
 	void SaveAnimSnapshots();
 	UAnimSequence* SaveAsAnimSequence(const FSnapshotAnimations& Recording, const FString& AnimName, const FString& FolderName);
 	void SetBonesAnimationInAnimSeq(const FSnapshotAnimations& Recording, UAnimSequence* AnimSequence);
@@ -128,6 +133,10 @@ protected:
 
 	FAdditionalOffsets LastAddOffsets;
 
+	FString SaveToDest = "";
+
+	float InitialPelvisFootDist = 0.;
+
 
 public:
 
@@ -167,11 +176,17 @@ public:
 	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Do Holding", Category = "MotionCapture Anim"))
 	bool DoHolding = false;
 
-	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Hold Scale Excess Time", Category = "MotionCapture Anim"))
-	float GestureHoldExcessTime = 0.f;
+	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Hold Scale Excess Start Time", Category = "MotionCapture Anim"))
+	float GestureHoldExcessStartTime = 0.f;
 
-	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Hold Excess Easing Exponent", Category = "MotionCapture Anim"))
-	float GestureHoldExcessEasingExponent = 2.f;
+	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Hold Scale Excess End Time", Category = "MotionCapture Anim"))
+	float GestureHoldExcessEndTime = 0.f;
+
+	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Hold Start Excess Easing Exponent", Category = "MotionCapture Anim"))
+	float GestureHoldStartExcessEasingExponent = 2.f;
+
+	UPROPERTY(EditAnywhere, meta = (DisplayName = "Gesture Hold End Excess Easing Exponent", Category = "MotionCapture Anim"))
+	float GestureHoldEndExcessEasingExponent = 2.f;
 
 	UPROPERTY(EditAnywhere, meta = (DisplayName = "Nr of Leg Smoothing Iterations", Category = "MotionCapture Anim"))
 	int LegSmoothTimes = 5;
@@ -182,6 +197,9 @@ public:
 	UPROPERTY(EditAnywhere, meta = (DisplayName = "Lock Feet To Green Foot Indicators", Category = "MotionCapture Anim"))
 	bool LockFeet = true;
 
+	UPROPERTY(EditAnywhere, meta = (DisplayName = "Cap On Pulling Down Body For Foot Stability", Category = "MotionCapture Anim"))
+	float MoveDownCap = 8.0f;
+
 	UPROPERTY(EditAnywhere, meta = (DisplayName = "Use Captured Hand Position", Category = "MotionCapture Anim"))
 	bool UseHandPosition = true;
 
diff --git a/Source/MoCapPlugin/Public/MCPawn.h b/Source/MoCapPlugin/Public/MCPawn.h
index 0666283..588b0eb 100644
--- a/Source/MoCapPlugin/Public/MCPawn.h
+++ b/Source/MoCapPlugin/Public/MCPawn.h
@@ -70,7 +70,7 @@ public:
 	void InputViveOffsetsToAnimInstance(TSharedPtr<FJsonObject> Data);
 	void InputFingerDataToAnimInstance(TSharedPtr<FJsonObject> Data);
 
-	void OffsetSensorData(EBodyPart part, FVector& Pos, FQuat& Rot);
+	void OffsetSensorData(EBodyPart part, FVector& Pos, FQuat& Rot, bool inverse = false);
 
 private:
 
-- 
GitLab