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