Select Git revision
MCController.cpp
Patrick Nossol authored
MCController.cpp 43.35 KiB
// Fill out your copyright notice in the Description page of Project Settings.
#include "MCController.h"
#include <string>
#include "Animation/PoseAsset.h"
#include "UObject/ConstructorHelpers.h"
#include "IUniversalLogging.h"
#include "Engine/Engine.h"
#include "Kismet/GameplayStatics.h"
#include "AssetRegistryModule.h"
AMCController::AMCController() {
PrimaryActorTick.bCanEverTick = true;
static ConstructorHelpers::FClassFinder<AActor> WidgetObjectClass1(TEXT("/MoCapPlugin/MCInstructionWidgetBPObject"));
InstructionWidgetObjClass = WidgetObjectClass1.Class;
static ConstructorHelpers::FClassFinder<AActor> WidgetObjectClass2(TEXT("/MoCapPlugin/MCFeedbackWidgetBPObject"));
FeedbackWidgetObjClass = WidgetObjectClass2.Class;
}
void AMCController::EndPlay(const EEndPlayReason::Type EndPlayReason) {
if (IsRecording) {
ToggleRecording();
}
}
void AMCController::BeginPlay() {
Super::BeginPlay();
APlayerController* Controller = UGameplayStatics::GetPlayerController(GetWorld(), 0);
EnableInput(Controller);
if (SetControls && InputComponent != nullptr) {
InputComponent->BindAction("ToggleRecording", EInputEvent::IE_Pressed, this, &AMCController::ToggleRecording);
InputComponent->BindAction("SetMarker", EInputEvent::IE_Pressed, this, &AMCController::SetMarker);
InputComponent->BindAction("StartRecording", EInputEvent::IE_Pressed, this, &AMCController::ToggleRecording);
InputComponent->BindAction("NextSentence", EInputEvent::IE_Pressed, this, &AMCController::SetMarker);
InputComponent->BindAction("SaveAnimation", EInputEvent::IE_Pressed, this, &AMCController::SaveAnimationEditor);
}
if (!Pawn || !Pawn->GetActorLocation().Equals(FVector(0, 0, 0)) || !Pawn->GetActorRotation().Equals(FRotator(0, 0, 0))) {
GEngine->AddOnScreenDebugMessage(-1, 30.0f, FColor::Red, FString::Printf(TEXT("Pawn for Recording in MCController is not positioned at 0, 0, 0 with rotation 0, 0, 0. This can lead to unwanted offsets."), *NameOfRecording));
}
Pawn->LoadMeasurementFile(MeasurementPath);
Pawn->GetAnimInstance()->PawnOwner = Pawn;
if (bFingerTrackingEnabled) {
USteamVRInputDeviceFunctionLibrary::SetCurlsAndSplaysState(true, true);
}
if (SpectatorCam && !UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled()) {
Pawn->VRCamera->SetWorldLocation(SpectatorCam->GetActorLocation());
Pawn->VRCamera->SetWorldRotation(SpectatorCam->GetActorRotation());
}
LastAddOffsets = AdditionalOffsets;
}
void AMCController::Tick(float DeltaTime) {
Super::Tick(DeltaTime);
if (!InstructionWidget) {
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), InstructionWidgetObjClass, FoundActors);
if (FoundActors.Num() > 0) {
InstructionWidgetObj = FoundActors[0];
UWidgetComponent* Component = Cast<UWidgetComponent>(FoundActors[0]->GetComponentByClass(UWidgetComponent::StaticClass()));
InstructionWidget = Cast<UMCInstructionWidget>(Component->GetUserWidgetObject());
if (!UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled()) {
InstructionWidget->SetVisibility(ESlateVisibility::Hidden);
}
}
}
if (!FeedbackWidget) {
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), FeedbackWidgetObjClass, FoundActors);
if (FoundActors.Num() > 0) {
UWidgetComponent* Component = Cast<UWidgetComponent>(FoundActors[0]->GetComponentByClass(UWidgetComponent::StaticClass()));
FeedbackWidget = Cast<UMCFeedbackWidget>(Component->GetUserWidgetObject());
}
}
else if(!FeedbackWidget->inited) {
FeedbackWidget->InitSensorBox(Pawn->SensorSetup);
}
if (SpectatorCam != nullptr && !SpectatorInited) {
USceneCaptureComponent2D* CaptureComponent = SpectatorCam->GetCaptureComponent2D();
CaptureComponent->HiddenActors = { InstructionWidgetObj };
if (CaptureComponent != nullptr && CaptureComponent->TextureTarget != nullptr) {
UHeadMountedDisplayFunctionLibrary::SetSpectatorScreenMode(ESpectatorScreenMode::Texture);
UHeadMountedDisplayFunctionLibrary::SetSpectatorScreenTexture(CaptureComponent->TextureTarget);
SpectatorInited = true;
}
}
if (!EditOffsetMode && IsSavingToAnim) {
SaveToAnimMode();
}
if (IsRecording) {
RecordMode();
}
LogHandler.WriteToLogFile();
}
void AMCController::RecordMode() {
TSharedPtr<FJsonObject> JsonObjectFull = MAKE_JSON;
JsonObjectFull->SetStringField("Type", "ViveData");
Pawn->AddSensorDataToJson(JsonObjectFull);
if (bFingerTrackingEnabled) {
Pawn->AddFingerDataToJson(JsonObjectFull, FingerTrackingMethod, FeedbackWidget);
}
FString OutputString = MCUtils::JsonToString(JsonObjectFull);
LogHandler.LogData(OutputString);
if (DebugMode) {
Pawn->InputViveDataToAnimInstance(JsonObjectFull);
if (bFingerTrackingEnabled) {
Pawn->InputFingerDataToAnimInstance(JsonObjectFull);
}
}
}
void AMCController::SaveToAnimMode() {
if (AnimSaveState.WaitForAnimInstance) {
if (AnimSaveState.Pawn->GetAnimInstance()->AppliedPose) {
AnimSaveState.WaitForAnimInstance = false;
}
}
if (!AnimSaveState.WaitForAnimInstance) {
if (AnimSaveState.CurrentEntryIndex < AnimSaveState.AnimData.Num()) {
InputNextFrame();
}
else {
SaveAnimSnapshots();
IsSavingToAnim = false;
//AnimSaveState.Pawn->Destroy();
AnimSaveState = FAnimSaveState();
if (InstructionWidget) {
InstructionWidget->FeedbackText->SetText(FText::FromString("Saved!"));
InstructionWidget->WidgetSwitcher->SetActiveWidgetIndex(InstructionWidget->StartIndex);
}
if (GEngine) {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Green, FString::Printf(TEXT("Animation Saved!")));
}
}
}
}
void AMCController::NextEditFrame() {
InputNextFrame();
}
void AMCController::PrevEditFrame() {
AnimSaveState.CurrentEntryIndex -= 2;
if (AnimSaveState.CurrentEntryIndex < 0) {
AnimSaveState.CurrentEntryIndex = 0;
}
InputNextFrame();
}
void AMCController::FinishEditAnim() {
IsSavingToAnim = false;
AnimSaveState = FAnimSaveState();
}
void AMCController::ScaleAnimDataInterval(int start, int end, float DeltaTime) {
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;
TArray<FProcessedAnimData> OldAnimData;
for (int i = 0; i < AnimSaveState.AnimData.Num(); i++) {
OldAnimData.Push(AnimSaveState.AnimData[i]);
}
for (int i = end + 1; i < AnimSaveState.AnimData.Num(); i++) {
AnimSaveState.AnimData[i].Timestamp += FTimespan::FromSeconds(DeltaTime);
}
AnimSaveState.AnimData.RemoveAt(start, OldNrFrames, true);
int CurOldIndex = start;
for (int i = 0; i < NewNrFrames; i++) {
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);
FTimespan OldTimestamp = StartTime + FTimespan::FromSeconds(TransformedI * OldTime);
bool stop = false;
while (CurOldIndex + 1 < OldAnimData.Num() && !(OldTimestamp >= OldAnimData[CurOldIndex].Timestamp && OldTimestamp <= OldAnimData[CurOldIndex + 1].Timestamp)) {
CurOldIndex++;
}
float AlphaTime;
if (CurOldIndex + 1 >= OldAnimData.Num() || OldAnimData[CurOldIndex + 1].IsEnd || OldAnimData[CurOldIndex + 1].IsMarker) {
AlphaTime = 0.f;
}
else if (OldAnimData[CurOldIndex].IsEnd || OldAnimData[CurOldIndex].IsMarker) {
AlphaTime = 1.f;
}
else {
AlphaTime = (OldTimestamp - OldAnimData[CurOldIndex].Timestamp).GetTotalSeconds() / (OldAnimData[CurOldIndex + 1].Timestamp - OldAnimData[CurOldIndex].Timestamp).GetTotalSeconds();
}
//process sensor data
FSensorData& SensorData = Data.SensorData;
for (int j = 0; j < EBodyPart::LAST; j++) {
EBodyPart Type = EBodyPart(j);
FSensorDataEntry* Entry = SensorData.GetEntry(Type);
const FSensorDataEntry* OldEntry0 = OldAnimData[CurOldIndex].SensorData.GetEntry(Type);
if (AlphaTime > 0.f) {
const FSensorDataEntry* OldEntry1 = OldAnimData[CurOldIndex + 1].SensorData.GetEntry(Type);
Entry->Pos = (1.f - AlphaTime) * OldEntry0->Pos + AlphaTime * OldEntry1->Pos;
Entry->Rot = FQuat::Slerp(OldEntry0->Rot, OldEntry1->Rot, AlphaTime);
Entry->valid = true;
Entry->PosUsed = true;
}
else {
Entry->Pos = OldEntry0->Pos;
Entry->Rot = OldEntry0->Rot;
Entry->valid = true;
Entry->PosUsed = true;
}
}
//process finger data
if (bFingerTrackingEnabled) {
FFingerData& FingerData = Data.FingerData;
for (int f = 0; f < 2; f++) {
FFingerDataEntry* Hand = f == 0 ? &FingerData.LeftHand : &FingerData.RightHand;
FFingerDataEntry* OldHand0 = f == 0 ? &OldAnimData[CurOldIndex].FingerData.LeftHand : &OldAnimData[CurOldIndex].FingerData.RightHand;
FFingerDataEntry* OldHand1 = AlphaTime > 0.f ? (f == 0 ? &OldAnimData[CurOldIndex + 1].FingerData.LeftHand : &OldAnimData[CurOldIndex + 1].FingerData.RightHand) : (&OldAnimData[CurOldIndex].FingerData.RightHand);
Hand->Thumb = (1.f - AlphaTime) * OldHand0->Thumb + AlphaTime * OldHand1->Thumb;
Hand->Index = (1.f - AlphaTime) * OldHand0->Index + AlphaTime * OldHand1->Index;
Hand->Middle = (1.f - AlphaTime) * OldHand0->Middle + AlphaTime * OldHand1->Middle;
Hand->Ring = (1.f - AlphaTime) * OldHand0->Ring + AlphaTime * OldHand1->Ring;
Hand->Pinky = (1.f - AlphaTime) * OldHand0->Pinky + AlphaTime * OldHand1->Pinky;
Hand->Thumb_Index = (1.f - AlphaTime) * OldHand0->Thumb_Index + AlphaTime * OldHand1->Thumb_Index;
Hand->Index_Middle = (1.f - AlphaTime) * OldHand0->Index_Middle + AlphaTime * OldHand1->Index_Middle;
Hand->Middle_Ring = (1.f - AlphaTime) * OldHand0->Middle_Ring + AlphaTime * OldHand1->Middle_Ring;
Hand->Ring_Pinky = (1.f - AlphaTime) * OldHand0->Ring_Pinky + AlphaTime * OldHand1->Ring_Pinky;
}
}
AnimSaveState.AnimData.Insert(Data, start + i);
}
}
bool AMCController::PreprocessRecording(const TArray<float>& HaltingPoints) {
UMCAnimInstance* AI = AnimSaveState.Pawn->GetAnimInstance();
//----translate json data to AnimSaveState.AnimData----
FTimespan LastStamp;
FTimespan NextFrame;
int CurrentMarker = 0;
float MaxPelvisSpineLength = -1.f;
for (int i = 0; i < AnimSaveState.StringData.Num(); i++) {
auto Entry = AnimSaveState.StringData[i];
FString TimeString, JsonString;
Entry.Split(" ", &TimeString, &JsonString);
FTimespan Timestamp = MCUtils::StringToTimespan(TimeString);
if (Timestamp < LastStamp) {
Timestamp = Timestamp + FTimespan::FromHours(1);
}
TSharedPtr<FJsonObject> TmpJson = MCUtils::StringToJson(JsonString);
if (!TmpJson->HasField("Type")) {
continue;
}
FString Type = TmpJson->GetStringField("Type");
if (Type == "Start") {
LastStamp = Timestamp;
NextFrame = Timestamp;
}
else if (Type == "Marker" || Type == "End") {
LastStamp = Timestamp;
NextFrame = Timestamp;
AnimSaveState.AnimData.Push(FProcessedAnimData());
AnimSaveState.AnimData.Last().Timestamp = Timestamp;
if (Type == "Marker") {
AnimSaveState.AnimData.Last().IsMarker = true;
CurrentMarker++;
}
else {
AnimSaveState.AnimData.Last().IsEnd = true;
}
}
else if (Type == "ViveData") {
AnimSaveState.Pawn->InputViveDataToAnimInstance(TmpJson);
if (bFingerTrackingEnabled) {
AnimSaveState.Pawn->InputFingerDataToAnimInstance(TmpJson);
}
if (CurrentMarker > 0 && Timestamp > NextFrame) {
AnimSaveState.AnimData.Push(FProcessedAnimData());
AnimSaveState.AnimData.Last().Timestamp = Timestamp;
AnimSaveState.AnimData.Last().SensorData = AI->SensorData;
if (bFingerTrackingEnabled) {
AnimSaveState.AnimData.Last().FingerData = AI->FingerData;
}
NextFrame = NextFrame + FTimespan::FromSeconds(AnimSaveState.SPF);
}
}
else if (Type == "Offsets") {
AnimSaveState.Pawn->InputViveOffsetsToAnimInstance(TmpJson);
}
}
//If the animation is empty, continue
if (AnimSaveState.AnimData.Num() <= 1) {
return false;
}
//----process the anim data----
//---Sensor turned off -> interpolate
for (int i = 0; i < AnimSaveState.AnimData.Num(); 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 (Entry->Pos.IsNearlyZero() && Entry->Rot.Rotator().IsNearlyZero()) {
FVector StartPos = Entry->Pos;
FQuat StartRot = Entry->Rot;
int StartIndex = i;
if (i > 0) {
StartPos = AnimSaveState.AnimData[i - 1].SensorData.GetEntry(Type)->Pos;
StartRot = AnimSaveState.AnimData[i - 1].SensorData.GetEntry(Type)->Rot;
StartIndex = i - 1;
}
FVector EndPos;
FQuat EndRot;
int EndIndex = AnimSaveState.AnimData.Num() - 1;
for (int k = i + 1; k < AnimSaveState.AnimData.Num(); k++) {
FSensorDataEntry* CurEntry = AnimSaveState.AnimData[k].SensorData.GetEntry(Type);
if (!CurEntry->Pos.IsNearlyZero() || !CurEntry->Rot.Rotator().IsNearlyZero()) {
EndPos = CurEntry->Pos;
EndRot = CurEntry->Rot;
EndIndex = k;
break;
}
}
float alphaStep = 1.f / (float)(EndIndex - StartIndex);
float alpha = alphaStep;
for (int k = StartIndex + 1; k < EndIndex; k++) {
FSensorDataEntry* CurEntry = AnimSaveState.AnimData[k].SensorData.GetEntry(Type);
CurEntry->Pos = (1.f - alpha) * StartPos + alpha * EndPos;
CurEntry->Rot = FQuat::Slerp(StartRot, EndRot, alpha);
//process finger data
if ((Type == EBodyPart::HandL || Type == EBodyPart::HandR) && bFingerTrackingEnabled) {
FFingerData& FingerData = AnimData.FingerData;
for (int f = 0; f < 2; f++) {
FFingerDataEntry* Hand = f == 0 ? &FingerData.LeftHand : &FingerData.RightHand;
FFingerDataEntry* HandStart = f == 0 ? &AnimSaveState.AnimData[StartIndex].FingerData.LeftHand : &AnimSaveState.AnimData[StartIndex].FingerData.RightHand;
FFingerDataEntry* HandEnd = f == 0 ? &AnimSaveState.AnimData[EndIndex].FingerData.LeftHand : &AnimSaveState.AnimData[EndIndex].FingerData.RightHand;
Hand->Thumb = (1.f - alpha) * HandStart->Thumb + alpha * HandEnd->Thumb;
Hand->Index = (1.f - alpha) * HandStart->Index + alpha * HandEnd->Index;
Hand->Middle = (1.f - alpha) * HandStart->Middle + alpha * HandEnd->Middle;
Hand->Ring = (1.f - alpha) * HandStart->Ring + alpha * HandEnd->Ring;
Hand->Pinky = (1.f - alpha) * HandStart->Pinky + alpha * HandEnd->Pinky;
Hand->Thumb_Index = (1.f - alpha) * HandStart->Thumb_Index + alpha * HandEnd->Thumb_Index;
Hand->Index_Middle = (1.f - alpha) * HandStart->Index_Middle + alpha * HandEnd->Index_Middle;
Hand->Middle_Ring = (1.f - alpha) * HandStart->Middle_Ring + alpha * HandEnd->Middle_Ring;
Hand->Ring_Pinky = (1.f - alpha) * HandStart->Ring_Pinky + alpha * HandEnd->Ring_Pinky;
}
}
alpha += alphaStep;
}
}
}
}
//---Smoothing of legs---
int smoothTimes = LegSmoothTimes;
for (int l = 0; l < smoothTimes; l++) {
TArray<FProcessedAnimData> OldAnimData = AnimSaveState.AnimData;
for (int i = 0; i < AnimSaveState.AnimData.Num(); i++) {
FProcessedAnimData& AnimData = AnimSaveState.AnimData[i];
if (AnimData.IsMarker || AnimData.IsEnd) {
continue;
}
TArray<EBodyPart> BodyParts = { EBodyPart::LowerBody, EBodyPart::FootL, EBodyPart::FootR };
for (EBodyPart BodyPart : BodyParts) {
FVector PosRes;
FQuat RotRes = OldAnimData[i].SensorData.GetEntry(BodyPart)->Rot;
int RotNum = 1;
int window = LegSmoothWindow;
int sum = 2 * window + 1;
FVector LastPos = OldAnimData[i].SensorData.GetEntry(BodyPart)->Pos;
FQuat LastRot = OldAnimData[i].SensorData.GetEntry(BodyPart)->Rot;
for (int k = 1; k < window; k++) {
int j = i - k;
if (j < 0 || OldAnimData[j].IsMarker) {
PosRes += ((window - k) / (float)sum) * LastPos;
RotRes = FQuat::Slerp(RotRes, LastRot, (float)RotNum / (float)(RotNum + (window - k)));
break;
}
else {
LastPos = OldAnimData[j].SensorData.GetEntry(BodyPart)->Pos;
PosRes += LastPos / (float)sum;
LastRot = OldAnimData[j].SensorData.GetEntry(BodyPart)->Rot;
RotRes = FQuat::Slerp(RotRes, LastRot, (float)RotNum / (RotNum + 1.f));
RotNum++;
}
}
LastPos = OldAnimData[i].SensorData.GetEntry(BodyPart)->Pos;
LastRot = OldAnimData[i].SensorData.GetEntry(BodyPart)->Rot;
for (int k = 0; k < window; k++) {
int j = i + k;
if (j >= OldAnimData.Num() || OldAnimData[j].IsMarker || OldAnimData[j].IsEnd) {
PosRes += ((window - k) / (float)sum) * LastPos;
RotRes = FQuat::Slerp(RotRes, LastRot, (float)RotNum / (float)(RotNum + (window - k)));
break;
}
else {
LastPos = OldAnimData[j].SensorData.GetEntry(BodyPart)->Pos;
PosRes += LastPos / (float)sum;
LastRot = OldAnimData[j].SensorData.GetEntry(BodyPart)->Rot;
RotRes = FQuat::Slerp(RotRes, LastRot, (float)RotNum / (RotNum + 1.f));
RotNum++;
}
}
if (BodyPart != EBodyPart::LowerBody) {
AnimSaveState.AnimData[i].SensorData.GetEntry(BodyPart)->Pos = PosRes;
}
AnimSaveState.AnimData[i].SensorData.GetEntry(BodyPart)->Rot = RotRes;
}
}
}
//---find last slow part of the gesture and scale it up---
TArray<float> GestureSpeed;
bool newMethod = true;
if (!newMethod) {
if (GestureHoldExcessTime > 0.f) {
for (int i = 1; i < AnimSaveState.AnimData.Num(); i++) {
FProcessedAnimData& AnimData = AnimSaveState.AnimData[i];
if (AnimData.IsMarker || AnimData.IsEnd) {
continue;
}
//process sensor data
float Speed = 0.f;
int SpeedCount = 0;
int window = 1;
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) {
for (int w = 0; w < window; w++) {
int index = i + w;
if (index - 1 >= 0 && index < AnimSaveState.AnimData.Num()) {
FProcessedAnimData& Data0 = AnimSaveState.AnimData[index - 1];
FProcessedAnimData& Data1 = AnimSaveState.AnimData[index];
float Time = (Data1.Timestamp - Data0.Timestamp).GetTotalSeconds();
Speed += Time * (Data1.SensorData.GetEntry(Type)->Pos - Data0.SensorData.GetEntry(Type)->Pos).Size();
}
}
SpeedCount++;
}
}
GestureSpeed.Push(Speed / (float)SpeedCount);
}
//smooth
TArray<float> SmoothedGestureSpeed;
int window = 5;
int Num = GestureSpeed.Num();
for (int i = 0; i < Num; i++) {
float avg = 0.f;
for (int wInd = -window; wInd <= window; wInd++) {
int w = i + wInd;
if (w < 0) {
avg += (GestureSpeed[0] + GestureSpeed[1] + GestureSpeed[2] + GestureSpeed[3] + GestureSpeed[4]) / 5.f;
}
else if (w >= Num) {
avg += (GestureSpeed[Num - 1] + GestureSpeed[Num - 2] + GestureSpeed[Num - 3] + GestureSpeed[Num - 4] + GestureSpeed[Num - 5]) / 5.f;
}
else {
avg += GestureSpeed[w];
}
}
avg /= (float)(window * 2 + 1);
SmoothedGestureSpeed.Push(avg);
}
GestureSpeed = SmoothedGestureSpeed;
int MaxNumToScale = 30;
//maximum
int maxWindow = 8;
int ScaleStart;
for (ScaleStart = GestureSpeed.Num() - 1; ScaleStart >= 0 && ScaleStart >= GestureSpeed.Num() - MaxNumToScale; ScaleStart--) {
bool isMax = true;
for (int w = -maxWindow; w <= maxWindow; w++) {
int index = ScaleStart + w;
if (index >= 0 && index < GestureSpeed.Num() && index != ScaleStart) {
if (GestureSpeed[index] >= GestureSpeed[ScaleStart]) {
isMax = false;
break;
}
}
}
if (isMax) {
break;
}
}
ScaleAnimDataInterval(ScaleStart, AnimSaveState.AnimData.Num() - 2, GestureHoldExcessTime);
}
}
else {
if (HaltingPoints.Num() > 0) {
FTimespan Start;
bool StartTimeInitialized = false;
bool hold = false;
int holdStartingIndex = -1;
for (int i = 1; i < AnimSaveState.AnimData.Num(); i++) {
FProcessedAnimData& AnimData = AnimSaveState.AnimData[i];
if (AnimData.IsMarker || AnimData.IsEnd) {
continue;
}
if (!StartTimeInitialized) {
StartTimeInitialized = true;
Start = AnimData.Timestamp;
}
//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 && hold) {
if (Type == EBodyPart::HandL || Type == EBodyPart::HandR) {
Entry->Pos = AnimSaveState.AnimData[i - 1].SensorData.GetEntry(Type)->Pos;
}
Entry->Rot = AnimSaveState.AnimData[i - 1].SensorData.GetEntry(Type)->Rot;
}
}
}
//or time unit?
if (!hold && HaltingPoints[0] <= (AnimData.Timestamp - Start).GetTotalSeconds()) {
hold = true;
holdStartingIndex = i;
}
}
if (hold && GestureHoldExcessTime > 0.f) {
ScaleAnimDataInterval(holdStartingIndex, AnimSaveState.AnimData.Num() - 2, GestureHoldExcessTime);
}
}
}
//---adjust feet position so that they are located at the green foot indicators---
int amount = 0;
int start = 0;
for (int i = 0; i < AnimSaveState.AnimData.Num() && LockFeet && LeftFootPlane && RightFootPlane; i++) {
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;
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);
cog += 0.5f * (LPos + RPos) * (1.f / amount);
}
FVector shiftAmount = 0.5f * (LeftFootPlane->GetActorLocation() + RightFootPlane->GetActorLocation()) - cog - FVector(0, 10, 0);
shiftAmount.Z = 0;
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 += shiftAmount;
}
}
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);
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;
}
amount = 0;
start = i + 1;
continue;
}
amount++;
}
AnimSaveState.NextFrame = FTimespan();
//debugging things
std::string speed = "";
for (int i = 0; i < GestureSpeed.Num(); i++) {
speed += "\n" + std::to_string(GestureSpeed[i]);
}
return true;
}
void AMCController::InputNextFrame() {
bool stop = false;
UMCAnimInstance* AI = AnimSaveState.Pawn->GetAnimInstance();
for (; !stop && AnimSaveState.CurrentEntryIndex < AnimSaveState.AnimData.Num();) {
auto& Entry = AnimSaveState.AnimData[AnimSaveState.CurrentEntryIndex];
AnimSaveState.CurrentEntryIndex++;
if (Entry.IsMarker || Entry.IsEnd) {
if (AnimSaveState.CurrentMarker != 0) {
AI->SnapshotAnimations.Last().Fps = AnimSaveState.FPS;
AI->SnapshotAnimations.Last().EndTime = Entry.Timestamp;
}
if (Entry.IsMarker) {
AI->SnapshotAnimations.Add(FSnapshotAnimations());
AI->SnapshotAnimations.Last().StartTime = Entry.Timestamp;
AnimSaveState.CurrentMarker++;
if (FeedbackWidget) {
FeedbackWidget->RecordingStateText->SetText(FText::FromString("Converting Anim " + FString::FromInt(AnimSaveState.CurrentMarker) + "..."));
}
}
else { // if (Type == "End")
AnimSaveState.CurrentEntryIndex++;
stop = true;
}
}
else {
AI->SensorData = Entry.SensorData;
if (bFingerTrackingEnabled) {
AI->FingerData = Entry.FingerData;
}
AI->SnapshotAnimations.Last().TimeStamps.Push(Entry.Timestamp);
AnimSaveState.Pawn->AICalcFrame();
stop = true;
if (!EditOffsetMode) {
AnimSaveState.WaitForAnimInstance = true;
}
if (FeedbackWidget) {
FeedbackWidget->ProgBar->SetPercent((float)AnimSaveState.CurrentEntryIndex / (float)AnimSaveState.AnimData.Num());
}
}
}
}
void AMCController::SaveAnimSnapshots() {
if (FeedbackWidget) {
FeedbackWidget->RecordingStateText->SetText(FText::FromString("Saving anim data to asset files..."));
}
const TArray<FSnapshotAnimations>& Anims = AnimSaveState.Pawn->GetAnimInstance()->SnapshotAnimations;
FString FolderName;
int j = 0;
do {
if (j == 0) {
FolderName = NameOfRecording;
}
else if (j < 10) {
FolderName = NameOfRecording + "_0" + FString::FromInt(j);
}
else {
FolderName = NameOfRecording + "_" + FString::FromInt(j);
}
j++;
} while (FPaths::DirectoryExists(FPaths::ProjectContentDir() + "/" + FolderName));
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
PlatformFile.CreateDirectory(*(FPaths::ProjectContentDir() + "/" + FolderName));
for (int i = 0; i < Anims.Num(); i++) {
FString AnimName;
if (i < 10) {
AnimName = NameOfRecording + "_0" + FString::FromInt(i + 1);
}
else {
AnimName = NameOfRecording + "_" + FString::FromInt(i + 1);
}
UAnimSequence* AnimSequence = SaveAsAnimSequence(Anims[i], AnimName, FolderName);
AnimSaveState.AnimSequences.Add(AnimSequence);
}
if (FeedbackWidget) {
FeedbackWidget->RecordingStateText->SetText(FText::FromString("Done!"));
FeedbackWidget->ProgBar->SetPercent(1);
}
}
UAnimSequence* AMCController::SaveAsAnimSequence(const FSnapshotAnimations& Recording, const FString& AnimName, const FString& FolderName) {
#if WITH_EDITOR
const FString AnimationRelative = FPaths::Combine(TEXT("/Game"), FolderName + "/" + AnimName);
const FString AnimationAbsolute = FPaths::Combine(FPaths::ProjectContentDir(), FolderName + "/" + AnimName);
UAnimSequence* AnimationSequence = nullptr;
UPackage* Package = LoadPackage(nullptr, *AnimationAbsolute, LOAD_None);
if (Package != nullptr) {
AnimationSequence = FindObject<UAnimSequence>(Package, *AnimName);
}
else {
Package = CreatePackage(*AnimationRelative);
}
if (AnimationSequence == nullptr) {
AnimationSequence = NewObject<UAnimSequence>(Package, UAnimSequence::StaticClass(), *AnimName, RF_Public | RF_Standalone);
if (AnimationSequence) {
AnimationSequence->SetSkeleton(AnimSaveState.Pawn->GetSkeleton());
int32 RefPelvisIndex = AnimSaveState.Pawn->SkeletalMesh->SkeletalMesh->RefSkeleton.FindBoneIndex("pelvis");
int32 PelvisIndex = AnimationSequence->GetSkeleton()->GetSkeletonBoneIndexFromMeshBoneIndex(AnimSaveState.Pawn->SkeletalMesh->SkeletalMesh, RefPelvisIndex);
AnimationSequence->GetSkeleton()->SetBoneTranslationRetargetingMode(PelvisIndex, EBoneTranslationRetargetingMode::Animation, false);
AnimationSequence->SetRawNumberOfFrame(Recording.Snapshots.Num());
AnimationSequence->SequenceLength = (Recording.EndTime - Recording.StartTime).GetTotalSeconds();
SetBonesAnimationInAnimSeq(Recording, AnimationSequence);
FAssetRegistryModule::AssetCreated(AnimationSequence);
const FString Filename = FString::Printf(TEXT("%s%s"), *AnimationAbsolute, *FPackageName::GetAssetPackageExtension());
UPackage::SavePackage(Package, nullptr, RF_Public | RF_Standalone, *Filename);
}
}
return AnimationSequence;
#else
UE_LOG(LogTemp, Error, TEXT("VHLipSync::SaveAsAnimSequence can only be used in Editor builds!"));
return nullptr;
#endif
}
void AMCController::SetBonesAnimationInAnimSeq(const FSnapshotAnimations& Recording, UAnimSequence* AnimSequence) {
#if WITH_EDITOR
TArray<FName> BoneNames;
for (int i = 0; i < Recording.Snapshots.Num(); i++) {
for (int j = 0; j < Recording.Snapshots[i].BoneNames.Num(); j++) {
if (!BoneNames.Contains(Recording.Snapshots[i].BoneNames[j])) {
BoneNames.Add(Recording.Snapshots[i].BoneNames[j]);
}
}
}
for (const FName& BoneName : BoneNames) {
FRawAnimSequenceTrack Track;
for (int Frame = 0; Frame < Recording.Snapshots.Num(); Frame++) {
const FPoseSnapshot& Snapshot = Recording.Snapshots[Frame];
int32 Index = Snapshot.BoneNames.IndexOfByKey(BoneName);
if (Index != INDEX_NONE) {
Track.RotKeys.Add(Snapshot.LocalTransforms[Index].GetRotation());
Track.PosKeys.Add(Snapshot.LocalTransforms[Index].GetLocation());
}
else if (Frame == 0) {
Track.RotKeys.Add(FRotator::ZeroRotator.Quaternion());
Track.PosKeys.Add(FVector::ZeroVector);
}
else {
Track.RotKeys.Add(Track.RotKeys[Frame - 1]);
Track.PosKeys.Add(Track.PosKeys[Frame - 1]);
}
}
AnimSequence->AddNewRawTrack(BoneName, &Track);
}
#endif
}
#if WITH_EDITOR
void AMCController::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) {
Super::PostEditChangeProperty(PropertyChangedEvent);
FProperty* prop = PropertyChangedEvent.Property;
FFloatProperty* fprop = CastField<FFloatProperty>(prop);
if (fprop != nullptr) {
float* val = nullptr;
{
if (LastAddOffsets.HeadRotOffset.Roll != AdditionalOffsets.HeadRotOffset.Roll) {
val = &AdditionalOffsets.HeadRotOffset.Roll;
}
if (LastAddOffsets.HeadRotOffset.Yaw != AdditionalOffsets.HeadRotOffset.Yaw) {
val = &AdditionalOffsets.HeadRotOffset.Yaw;
}
if (LastAddOffsets.HeadRotOffset.Pitch != AdditionalOffsets.HeadRotOffset.Pitch) {
val = &AdditionalOffsets.HeadRotOffset.Pitch;
}
if (LastAddOffsets.HeadPosOffset.X != AdditionalOffsets.HeadPosOffset.X) {
val = &AdditionalOffsets.HeadPosOffset.X;
}
if (LastAddOffsets.HeadPosOffset.Y != AdditionalOffsets.HeadPosOffset.Y) {
val = &AdditionalOffsets.HeadPosOffset.Y;
}
if (LastAddOffsets.HeadPosOffset.Z != AdditionalOffsets.HeadPosOffset.Z) {
val = &AdditionalOffsets.HeadPosOffset.Z;
}
if (LastAddOffsets.HandLRotOffset.Roll != AdditionalOffsets.HandLRotOffset.Roll) {
val = &AdditionalOffsets.HandLRotOffset.Roll;
}
if (LastAddOffsets.HandLRotOffset.Yaw != AdditionalOffsets.HandLRotOffset.Yaw) {
val = &AdditionalOffsets.HandLRotOffset.Yaw;
}
if (LastAddOffsets.HandLRotOffset.Pitch != AdditionalOffsets.HandLRotOffset.Pitch) {
val = &AdditionalOffsets.HandLRotOffset.Pitch;
}
if (LastAddOffsets.HandLPosOffset.X != AdditionalOffsets.HandLPosOffset.X) {
val = &AdditionalOffsets.HandLPosOffset.X;
}
if (LastAddOffsets.HandLPosOffset.Y != AdditionalOffsets.HandLPosOffset.Y) {
val = &AdditionalOffsets.HandLPosOffset.Y;
}
if (LastAddOffsets.HandLPosOffset.Z != AdditionalOffsets.HandLPosOffset.Z) {
val = &AdditionalOffsets.HandLPosOffset.Z;
}
if (LastAddOffsets.HandRRotOffset.Roll != AdditionalOffsets.HandRRotOffset.Roll) {
val = &AdditionalOffsets.HandRRotOffset.Roll;
}
if (LastAddOffsets.HandRRotOffset.Yaw != AdditionalOffsets.HandRRotOffset.Yaw) {
val = &AdditionalOffsets.HandRRotOffset.Yaw;
}
if (LastAddOffsets.HandRRotOffset.Pitch != AdditionalOffsets.HandRRotOffset.Pitch) {
val = &AdditionalOffsets.HandRRotOffset.Pitch;
}
if (LastAddOffsets.HandRPosOffset.X != AdditionalOffsets.HandRPosOffset.X) {
val = &AdditionalOffsets.HandRPosOffset.X;
}
if (LastAddOffsets.HandRPosOffset.Y != AdditionalOffsets.HandRPosOffset.Y) {
val = &AdditionalOffsets.HandRPosOffset.Y;
}
if (LastAddOffsets.HandRPosOffset.Z != AdditionalOffsets.HandRPosOffset.Z) {
val = &AdditionalOffsets.HandRPosOffset.Z;
}
if (LastAddOffsets.LowerLegRRotOffset.Roll != AdditionalOffsets.LowerLegRRotOffset.Roll) {
val = &AdditionalOffsets.LowerLegRRotOffset.Roll;
}
if (LastAddOffsets.LowerLegRRotOffset.Yaw != AdditionalOffsets.LowerLegRRotOffset.Yaw) {
val = &AdditionalOffsets.LowerLegRRotOffset.Yaw;
}
if (LastAddOffsets.LowerLegRRotOffset.Pitch != AdditionalOffsets.LowerLegRRotOffset.Pitch) {
val = &AdditionalOffsets.LowerLegRRotOffset.Pitch;
}
if (LastAddOffsets.LowerLegRPosOffset.X != AdditionalOffsets.LowerLegRPosOffset.X) {
val = &AdditionalOffsets.LowerLegRPosOffset.X;
}
if (LastAddOffsets.LowerLegRPosOffset.Y != AdditionalOffsets.LowerLegRPosOffset.Y) {
val = &AdditionalOffsets.LowerLegRPosOffset.Y;
}
if (LastAddOffsets.LowerLegRPosOffset.Z != AdditionalOffsets.LowerLegRPosOffset.Z) {
val = &AdditionalOffsets.LowerLegRPosOffset.Z;
}
if (LastAddOffsets.LowerLegLRotOffset.Roll != AdditionalOffsets.LowerLegLRotOffset.Roll) {
val = &AdditionalOffsets.LowerLegLRotOffset.Roll;
}
if (LastAddOffsets.LowerLegLRotOffset.Yaw != AdditionalOffsets.LowerLegLRotOffset.Yaw) {
val = &AdditionalOffsets.LowerLegLRotOffset.Yaw;
}
if (LastAddOffsets.LowerLegLRotOffset.Pitch != AdditionalOffsets.LowerLegLRotOffset.Pitch) {
val = &AdditionalOffsets.LowerLegLRotOffset.Pitch;
}
if (LastAddOffsets.LowerLegLPosOffset.X != AdditionalOffsets.LowerLegLPosOffset.X) {
val = &AdditionalOffsets.LowerLegLPosOffset.X;
}
if (LastAddOffsets.LowerLegLPosOffset.Y != AdditionalOffsets.LowerLegLPosOffset.Y) {
val = &AdditionalOffsets.LowerLegLPosOffset.Y;
}
if (LastAddOffsets.LowerLegLPosOffset.Z != AdditionalOffsets.LowerLegLPosOffset.Z) {
val = &AdditionalOffsets.LowerLegLPosOffset.Z;
}
if (LastAddOffsets.LowerArmLRotOffset.Roll != AdditionalOffsets.LowerArmLRotOffset.Roll) {
val = &AdditionalOffsets.LowerArmLRotOffset.Roll;
}
if (LastAddOffsets.LowerArmLRotOffset.Yaw != AdditionalOffsets.LowerArmLRotOffset.Yaw) {
val = &AdditionalOffsets.LowerArmLRotOffset.Yaw;
}
if (LastAddOffsets.LowerArmLRotOffset.Pitch != AdditionalOffsets.LowerArmLRotOffset.Pitch) {
val = &AdditionalOffsets.LowerArmLRotOffset.Pitch;
}
if (LastAddOffsets.LowerArmLPosOffset.X != AdditionalOffsets.LowerArmLPosOffset.X) {
val = &AdditionalOffsets.LowerArmLPosOffset.X;
}
if (LastAddOffsets.LowerArmLPosOffset.Y != AdditionalOffsets.LowerArmLPosOffset.Y) {
val = &AdditionalOffsets.LowerArmLPosOffset.Y;
}
if (LastAddOffsets.LowerArmLPosOffset.Z != AdditionalOffsets.LowerArmLPosOffset.Z) {
val = &AdditionalOffsets.LowerArmLPosOffset.Z;
}
if (LastAddOffsets.LowerArmRRotOffset.Roll != AdditionalOffsets.LowerArmRRotOffset.Roll) {
val = &AdditionalOffsets.LowerArmRRotOffset.Roll;
}
if (LastAddOffsets.LowerArmRRotOffset.Yaw != AdditionalOffsets.LowerArmRRotOffset.Yaw) {
val = &AdditionalOffsets.LowerArmRRotOffset.Yaw;
}
if (LastAddOffsets.LowerArmRRotOffset.Pitch != AdditionalOffsets.LowerArmRRotOffset.Pitch) {
val = &AdditionalOffsets.LowerArmRRotOffset.Pitch;
}
if (LastAddOffsets.LowerArmRPosOffset.X != AdditionalOffsets.LowerArmRPosOffset.X) {
val = &AdditionalOffsets.LowerArmRPosOffset.X;
}
if (LastAddOffsets.LowerArmRPosOffset.Y != AdditionalOffsets.LowerArmRPosOffset.Y) {
val = &AdditionalOffsets.LowerArmRPosOffset.Y;
}
if (LastAddOffsets.LowerArmRPosOffset.Z != AdditionalOffsets.LowerArmRPosOffset.Z) {
val = &AdditionalOffsets.LowerArmRPosOffset.Z;
}
if (LastAddOffsets.LowerBodyRotOffset.Roll != AdditionalOffsets.LowerBodyRotOffset.Roll) {
val = &AdditionalOffsets.LowerBodyRotOffset.Roll;
}
if (LastAddOffsets.LowerBodyRotOffset.Yaw != AdditionalOffsets.LowerBodyRotOffset.Yaw) {
val = &AdditionalOffsets.LowerBodyRotOffset.Yaw;
}
if (LastAddOffsets.LowerBodyRotOffset.Pitch != AdditionalOffsets.LowerBodyRotOffset.Pitch) {
val = &AdditionalOffsets.LowerBodyRotOffset.Pitch;
}
if (LastAddOffsets.LowerBodyPosOffset.X != AdditionalOffsets.LowerBodyPosOffset.X) {
val = &AdditionalOffsets.LowerBodyPosOffset.X;
}
if (LastAddOffsets.LowerBodyPosOffset.Y != AdditionalOffsets.LowerBodyPosOffset.Y) {
val = &AdditionalOffsets.LowerBodyPosOffset.Y;
}
if (LastAddOffsets.LowerBodyPosOffset.Z != AdditionalOffsets.LowerBodyPosOffset.Z) {
val = &AdditionalOffsets.LowerBodyPosOffset.Z;
}
if (LastAddOffsets.UpperBodyRotOffset.Roll != AdditionalOffsets.UpperBodyRotOffset.Roll) {
val = &AdditionalOffsets.UpperBodyRotOffset.Roll;
}
if (LastAddOffsets.UpperBodyRotOffset.Yaw != AdditionalOffsets.UpperBodyRotOffset.Yaw) {
val = &AdditionalOffsets.UpperBodyRotOffset.Yaw;
}
if (LastAddOffsets.UpperBodyRotOffset.Pitch != AdditionalOffsets.UpperBodyRotOffset.Pitch) {
val = &AdditionalOffsets.UpperBodyRotOffset.Pitch;
}
if (LastAddOffsets.UpperBodyPosOffset.X != AdditionalOffsets.UpperBodyPosOffset.X) {
val = &AdditionalOffsets.UpperBodyPosOffset.X;
}
if (LastAddOffsets.UpperBodyPosOffset.Y != AdditionalOffsets.UpperBodyPosOffset.Y) {
val = &AdditionalOffsets.UpperBodyPosOffset.Y;
}
if (LastAddOffsets.UpperBodyPosOffset.Z != AdditionalOffsets.UpperBodyPosOffset.Z) {
val = &AdditionalOffsets.UpperBodyPosOffset.Z;
}
}
if (val != nullptr) {
UObject* DefaultObject = GetClass()->GetDefaultObject();
//fprop->SetPropertyValue_InContainer(DefaultObject, *val);
LastAddOffsets = AdditionalOffsets;
}
}
}
#endif
void AMCController::ToggleRecording() {
if (IsSavingToAnim) {
return;
}
if (!IsRecording) {
LogHandler.NewLog("DataLog" + FString::FromInt(CurRecordingInSession));
LogHandler.StartRecording();
Pawn->CalcSensorOffsets(LogHandler, UseLastOffsets, DebugMode);
if (DebugMode || KeepPawnVisible) {
Pawn->SetMeshVisibility(true);
}
else {
Pawn->SetMeshVisibility(false);
}
AnimSaveState.CurrentMarker = 0;
if (InstructionWidget) {
InstructionWidget->WidgetSwitcher->SetActiveWidgetIndex(InstructionWidget->RecordIndex);
}
if (FeedbackWidget) {
FeedbackWidget->RecordingStateText->SetText(FText::FromString("Press 'SetMarker' to begin!"));
FeedbackWidget->RecordingStateText->SetColorAndOpacity(FSlateColor(FLinearColor::White));
FeedbackWidget->SensorBox->SetVisibility(ESlateVisibility::Visible);
FeedbackWidget->AnimNameText->SetText(FText::FromString(NameOfRecording));
FeedbackWidget->ProgBar->SetVisibility(ESlateVisibility::Hidden);
}
}
else {
if (!KeepPawnInvisible) {
Pawn->SetMeshVisibility(true);
}
LogHandler.StopRecording();
LogHandler.WriteToLogFile();
FString OldName = NameOfRecording;
LogHandler.CopyLogToRecordings(NameOfRecording);
if (OldName != NameOfRecording) {
if (InstructionWidget) {
InstructionWidget->FeedbackText->SetText(FText::FromString("Recording Done!\nNameOfRecording changed to " + NameOfRecording));
}
if (OutputMsgOnScreen) {
GEngine->AddOnScreenDebugMessage(-1, 30.0f, FColor::Yellow, FString::Printf(TEXT("NameOfRecording changed to %s. If you want to use this recording in future play sessions, please update the property on MCController."), *NameOfRecording));
}
}
if (InstructionWidget) {
InstructionWidget->FeedbackText->SetText(FText::FromString("Recording Done!"));
InstructionWidget->WidgetSwitcher->SetActiveWidgetIndex(InstructionWidget->StartIndex);
}
if (FeedbackWidget) {
FeedbackWidget->RecordingStateText->SetText(FText::FromString(""));
FeedbackWidget->SensorBox->SetVisibility(ESlateVisibility::Hidden);
FeedbackWidget->LeftFingerText->SetText(FText::FromString(""));
FeedbackWidget->RightFingerText->SetText(FText::FromString(""));
FeedbackWidget->AnimNameText->SetText(FText::FromString(""));
}
CurRecordingInSession++;
}
if (DebugMode) {
Pawn->GetAnimInstance()->DoRig = true;
}
else {
Pawn->GetAnimInstance()->DoRig = false;
}
IsRecording = !IsRecording;
}
void AMCController::SetMarker() {
if (IsRecording) {
AnimSaveState.CurrentMarker++;
LogHandler.SetMarker();
if (FeedbackWidget) {
FeedbackWidget->RecordingStateText->SetText(FText::FromString("Recording Anim " + FString::FromInt(AnimSaveState.CurrentMarker)));
FeedbackWidget->RecordingStateText->SetColorAndOpacity(FSlateColor(FLinearColor::Yellow));
}
}
}
void AMCController::SaveAnimationEditor() {
SaveAnimation({}, false);
}
void AMCController::SaveAnimation(const TArray<float>& HaltingPoints, bool skipTranslation) {
if (IsSavingToAnim) {
return;
}
if (IsRecording) {
ToggleRecording();
}
AnimSaveState = FAnimSaveState();
FString PathSaved = FPaths::ProjectSavedDir() + "Recordings/" + NameOfRecording;
if (!FPaths::DirectoryExists(PathSaved)) {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString::Printf(TEXT("Recording Path does not exist!")));
return;
}
if (!FPaths::FileExists(PathSaved + "/DataLog.log")) {
if (!FPaths::FileExists(PathSaved + "/LogSourcePath.txt")) {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString::Printf(TEXT("Recording Path Folder is not complete!")));
return;
}
FString LogSourcePath;
FFileHelper::LoadFileToString(LogSourcePath, *(PathSaved + "/LogSourcePath.txt"));
LogSourcePath = FPaths::ProjectSavedDir() + "OwnLogs/" + LogSourcePath;
if (!FPaths::FileExists(LogSourcePath)) {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString::Printf(TEXT("The corresponding Log to this recording name in OwnLogs is not existent!")));
return;
}
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
PlatformFile.CopyFile(*(PathSaved + "/DataLog.log"), *LogSourcePath);
PathSaved = LogSourcePath;
}
else {
PathSaved = PathSaved + "/DataLog.log";
}
if (InstructionWidget) {
InstructionWidget->WidgetSwitcher->SetActiveWidgetIndex(InstructionWidget->SavingIndex);
}
if (GEngine) {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Green, FString::Printf(TEXT("Saving Animation...")));
}
if (FeedbackWidget) {
FeedbackWidget->ProgBar->SetVisibility(ESlateVisibility::Visible);
FeedbackWidget->ProgBar->SetPercent(0);
FeedbackWidget->AnimNameText->SetText(FText::FromString(NameOfRecording));
}
FFileHelper::LoadFileToStringArray(AnimSaveState.StringData, *PathSaved);
AnimSaveState.CurrentEntryIndex = 0;
AnimSaveState.CurrentMarker = 0;
AnimSaveState.Pawn = Pawn;
AnimSaveState.Pawn->GetAnimInstance()->DoFingers = bFingerTrackingEnabled;
AnimSaveState.Pawn->GetAnimInstance()->LockFeet = LockFeet;
AnimSaveState.Pawn->GetAnimInstance()->UseHandPos = UseHandPosition;
AnimSaveState.Pawn->GetAnimInstance()->LimitHandRot = LimitHandRotation;
AnimSaveState.Pawn->GetAnimInstance()->SnapshotAnimations.Empty();
AnimSaveState.FPS = FramesPerSecond;
AnimSaveState.SPF = 1.f / ((float)AnimSaveState.FPS);
IsSavingToAnim = !skipTranslation;
if (skipTranslation) {
return;
}
if (!EditOffsetMode) {
AnimSaveState.WaitForAnimInstance = true;
}
AnimSaveState.Pawn->GetAnimInstance()->AdditionalOffsets = AdditionalOffsets;
bool isValid = PreprocessRecording(HaltingPoints);
if (!isValid) {
IsSavingToAnim = false;
return;
}
AnimSaveState.Pawn->GetAnimInstance()->DoRig = true;
InputNextFrame();
}