Skip to content
Snippets Groups Projects
Commit 1e5c73a6 authored by Jonathan Ehret's avatar Jonathan Ehret
Browse files

Merge branch 'feature/gaze_logging' into '4.26'

GazeTracking Logging

See merge request !2
parents 10adc950 63aae672
No related branches found
No related tags found
1 merge request!2GazeTracking Logging
Showing
with 147 additions and 44 deletions
#include "GazeTracking/SFGazeTracker.h" #include "GazeTracking/SFGazeTracker.h"
#include "Logging/SFLoggingBPLibrary.h"
#include "SFGameInstance.h" #include "SFGameInstance.h"
#include "GazeTracking/SFGazeTarget.h" #include "GazeTracking/SFGazeTarget.h"
#include "GazeTracking/SFGazeTargetActor.h" #include "GazeTracking/SFGazeTargetActor.h"
...@@ -26,7 +26,7 @@ void USFGazeTracker::Init(EGazeTrackerMode Mode) ...@@ -26,7 +26,7 @@ void USFGazeTracker::Init(EGazeTrackerMode Mode)
} }
else else
{ {
USFGameInstance::Get()->LogComment("No Vive Pro Eye present, use head rotation only for gaze tracking."); USFLoggingBPLibrary::LogComment("No Vive Pro Eye present, use head rotation only for gaze tracking.", true);
} }
#else #else
FSFUtils::OpenMessageBox("SRanipal Plugin is not present, cannot use eye tracking! Check out, e.g., StudyFramework Wiki where to get it", true); FSFUtils::OpenMessageBox("SRanipal Plugin is not present, cannot use eye tracking! Check out, e.g., StudyFramework Wiki where to get it", true);
...@@ -34,10 +34,11 @@ void USFGazeTracker::Init(EGazeTrackerMode Mode) ...@@ -34,10 +34,11 @@ void USFGazeTracker::Init(EGazeTrackerMode Mode)
} }
} }
FGazeRay USFGazeTracker::GetGazeDirection() FGazeRay USFGazeTracker::GetLocalGazeDirection()
{ {
if(!IsTrackingEyes()) if(!bEyeTrackingStarted)
{ {
// if no eye tracker is used we always "look ahead"
FGazeRay GazeRay; FGazeRay GazeRay;
GazeRay.Origin = FVector::ZeroVector; GazeRay.Origin = FVector::ZeroVector;
GazeRay.Direction = FVector::ForwardVector;; GazeRay.Direction = FVector::ForwardVector;;
...@@ -54,14 +55,9 @@ FGazeRay USFGazeTracker::GetGazeDirection() ...@@ -54,14 +55,9 @@ FGazeRay USFGazeTracker::GetGazeDirection()
return GazeRay; return GazeRay;
} }
FString USFGazeTracker::GetCurrentGazeTarget() FGazeRay USFGazeTracker::GetWorldGazeDirection()
{ {
FString GazedAtTarget = ""; FGazeRay LocalGazeRay = GetLocalGazeDirection();
const float Distance = 1000.0f;
FGazeRay GazeRay = GetGazeDirection();
// if no eye tracker is used we always "look ahead"
// GazeDirection = FVector(1,0,0);
UWorld* World = USFGameInstance::Get()->GetWorld(); UWorld* World = USFGameInstance::Get()->GetWorld();
...@@ -70,13 +66,41 @@ FString USFGazeTracker::GetCurrentGazeTarget() ...@@ -70,13 +66,41 @@ FString USFGazeTracker::GetCurrentGazeTarget()
PlayerCameraManager; PlayerCameraManager;
const FVector CameraLocation = CamManager->GetCameraLocation(); const FVector CameraLocation = CamManager->GetCameraLocation();
const FRotator CameraRotation = CamManager->GetCameraRotation(); const FRotator CameraRotation = CamManager->GetCameraRotation();
const FVector RayCastOrigin = CameraLocation + CameraRotation.RotateVector(GazeRay.Origin);
const FVector RayCastEnd = (CameraRotation.RotateVector(GazeRay.Direction) * Distance) + RayCastOrigin; FGazeRay GazeRay;
GazeRay.Origin = CameraLocation + CameraRotation.RotateVector(LocalGazeRay.Origin);
GazeRay.Direction = CameraRotation.RotateVector(LocalGazeRay.Direction);
return GazeRay;
}
FString USFGazeTracker::GetCurrentGazeTarget()
{
FString GazedAtTarget = "";
const float Distance = 1000.0f;
FGazeRay GazeRay = GetWorldGazeDirection();
const FVector RayCastOrigin = GazeRay.Origin;
const FVector RayCastEnd = (GazeRay.Direction * Distance) + RayCastOrigin;
//FSFUtils::Log("Cast Ray from "+RayCastOrigin.ToString()+" to "+RayCastEnd.ToString()); //FSFUtils::Log("Cast Ray from "+RayCastOrigin.ToString()+" to "+RayCastEnd.ToString());
FHitResult HitResult; FHitResult HitResult;
World->LineTraceSingleByChannel(HitResult, RayCastOrigin, RayCastEnd, EYE_TRACKING_TRACE_CHANNEL); UWorld* World = USFGameInstance::Get()->GetWorld();
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(World->GetFirstPlayerController()->AcknowledgedPawn);
QueryParams.AddIgnoredActor(USFGameInstance::Get()->GetHUD()->GetHUDHelper());
World->LineTraceSingleByChannel(HitResult, RayCastOrigin, RayCastEnd, EYE_TRACKING_TRACE_CHANNEL, QueryParams);
if (bDebugRenderRayTraces)
{
//this line trace is more comfortable for debug drawing, however has problems with channel tracing, so we use LineTraceSingleByChannel() above
FHitResult TmpHitRes;
UKismetSystemLibrary::LineTraceSingle(World, RayCastOrigin, RayCastEnd, ETraceTypeQuery::TraceTypeQuery4, false, {}, EDrawDebugTrace::ForDuration, TmpHitRes, true);
}
if (HitResult.bBlockingHit) if (HitResult.bBlockingHit)
...@@ -104,17 +128,22 @@ FString USFGazeTracker::GetCurrentGazeTarget() ...@@ -104,17 +128,22 @@ FString USFGazeTracker::GetCurrentGazeTarget()
void USFGazeTracker::LaunchCalibration() void USFGazeTracker::LaunchCalibration()
{ {
#ifdef WITH_SRANIPAL #ifdef WITH_SRANIPAL
//TODO: not tested yet!
ViveSR::anipal::Eye::LaunchEyeCalibration(nullptr); ViveSR::anipal::Eye::LaunchEyeCalibration(nullptr);
#endif #endif
} }
bool USFGazeTracker::IsTrackingEyes() bool USFGazeTracker::IsTrackingEyes()
{ {
//TODO: maybe use if (!bEyeTrackingStarted)
//#ifdef WITH_SRANIPAL return false;
//ViveSR::anipal::AnipalStatus Status;
//int Error = ViveSR::anipal::GetStatus(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE_V2, &Status); #ifdef WITH_SRANIPAL
//#endif ViveSR::anipal::Eye::EyeData_v2 Data;
return bEyeTrackingStarted; ViveSR::anipal::Eye::GetEyeData_v2(&Data);
//no_user apparently is true, when a user is there... (weird but consistent)
return Data.no_user;
#endif
return true;
} }
...@@ -243,3 +243,7 @@ void ASFMasterHUD::OnShowConditionsButtonPressed() ...@@ -243,3 +243,7 @@ void ASFMasterHUD::OnShowConditionsButtonPressed()
} }
} }
} }
const ASFHMDSpectatorHUDHelp* ASFMasterHUD::GetHUDHelper() {
return HMDHUDHelper;
}
...@@ -50,7 +50,7 @@ void USFLogObject::Initialize() { ...@@ -50,7 +50,7 @@ void USFLogObject::Initialize() {
} }
// NOTE: When changing header row, update output (see below) // NOTE: When changing header row, update output (see below)
void USFLogObject::LogHeaderRows() { void USFLogObject::WritePositionLogHeaderRow() {
FString PositionLogHeader = FString("ElapsedTime") + FString PositionLogHeader = FString("ElapsedTime") +
"\t" + FString("LogName") + "\t" + FString("LogName") +
"\t" + FString("Condition") + "\t" + FString("Condition") +
...@@ -63,7 +63,7 @@ void USFLogObject::LogHeaderRows() { ...@@ -63,7 +63,7 @@ void USFLogObject::LogHeaderRows() {
UniLog.Log(PositionLogHeader, "PositionLog"); UniLog.Log(PositionLogHeader, "PositionLog");
} }
void USFLogObject::LoggingLoopsLogToFile() { void USFLogObject::WritePositionLogToFile() {
if (!USFGameInstance::Get() || !USFGameInstance::Get()->GetLogObject()) if (!USFGameInstance::Get() || !USFGameInstance::Get()->GetLogObject())
{ {
return; return;
...@@ -73,14 +73,14 @@ void USFLogObject::LoggingLoopsLogToFile() { ...@@ -73,14 +73,14 @@ void USFLogObject::LoggingLoopsLogToFile() {
if (ComponentLoggingInfo.LogNextTick == true) { if (ComponentLoggingInfo.LogNextTick == true) {
ComponentLoggingInfo.LogNextTick = false; ComponentLoggingInfo.LogNextTick = false;
//When starting in Debug-Mode (i.e. not through the HUD) no condition is defined. //When starting in Debug-Mode (i.e. not through the HUD) no condition is defined.
FString currentCondition = USFGameInstance::Get()->GetParticipant()->GetCurrentCondition() ? FString CurrentCondition = USFGameInstance::Get()->GetParticipant()->GetCurrentCondition() ?
USFGameInstance::Get()->GetParticipant()->GetCurrentCondition()->UniqueName : USFGameInstance::Get()->GetParticipant()->GetCurrentCondition()->UniqueName :
FString("Debug"); FString("Debug");
// NOTE: When changing output, update header row (see above) // NOTE: When changing output, update header row (see above)
FString out = USFGameInstance::Get()->GetParticipant()->GetCurrentTime() + FString out = USFGameInstance::Get()->GetParticipant()->GetCurrentTime() +
"\t" + ComponentLoggingInfo.LogName + "\t" + ComponentLoggingInfo.LogName +
"\t" + currentCondition + "\t" + CurrentCondition +
"\t" + FString::Printf(TEXT("%.3f"), ComponentLoggingInfo.ComponentToLog->GetComponentLocation().X) + "\t" + FString::Printf(TEXT("%.3f"), ComponentLoggingInfo.ComponentToLog->GetComponentLocation().X) +
"\t" + FString::Printf(TEXT("%.3f"), ComponentLoggingInfo.ComponentToLog->GetComponentLocation().Y) + "\t" + FString::Printf(TEXT("%.3f"), ComponentLoggingInfo.ComponentToLog->GetComponentLocation().Y) +
"\t" + FString::Printf(TEXT("%.3f"), ComponentLoggingInfo.ComponentToLog->GetComponentLocation().Z) + "\t" + FString::Printf(TEXT("%.3f"), ComponentLoggingInfo.ComponentToLog->GetComponentLocation().Z) +
...@@ -99,6 +99,45 @@ void USFLogObject::LoggingLoopsLogToFile() { ...@@ -99,6 +99,45 @@ void USFLogObject::LoggingLoopsLogToFile() {
} }
} }
// NOTE: When changing header row, update output (see below)
void USFLogObject::WriteGazeTrackingLogHeaderRow() {
FString GazeTrackingLogHeader = FString("ElapsedTime") +
"\t" + FString("Condition") +
"\t" + FString("TrackingEyes") +
"\t" + FString("GazeTarget") +
"\t" + FString("Gaze-Origin-X-Y-Z") +
"\t" + FString("Gaze-Direction-X-Y-Z");
UniLog.Log(GazeTrackingLogHeader, "GazeTrackingLog");
}
void USFLogObject::WriteGazeTrackingLogToFile() {
if (!USFGameInstance::Get() || !USFGameInstance::Get()->GetGazeTracker())
{
return;
}
USFGazeTracker* GazeTracker = USFGameInstance::Get()->GetGazeTracker();
FString GazeTarget = GazeTracker->GetCurrentGazeTarget() == "" ? "-" : GazeTracker->GetCurrentGazeTarget();
//When starting in Debug-Mode (i.e. not through the HUD) no condition is defined.
FString CurrentCondition = USFGameInstance::Get()->GetParticipant()->GetCurrentCondition() ?
USFGameInstance::Get()->GetParticipant()->GetCurrentCondition()->UniqueName :
FString("Debug");
FString isTrackingEyes = USFGameInstance::Get()->GetGazeTracker()->IsTrackingEyes() ? "1" : "0";
// NOTE: When changing output, update header row (see above)
FString out = USFGameInstance::Get()->GetParticipant()->GetCurrentTime() +
"\t" + CurrentCondition +
"\t" + isTrackingEyes +
"\t" + GazeTarget +
"\t" + FString::Printf(TEXT("%.3f"), GazeTracker->GetWorldGazeDirection().Origin.X) +
"\t" + FString::Printf(TEXT("%.3f"), GazeTracker->GetWorldGazeDirection().Origin.Y) +
"\t" + FString::Printf(TEXT("%.3f"), GazeTracker->GetWorldGazeDirection().Origin.Z) +
"\t" + FString::Printf(TEXT("%.3f"), GazeTracker->GetWorldGazeDirection().Direction.X) +
"\t" + FString::Printf(TEXT("%.3f"), GazeTracker->GetWorldGazeDirection().Direction.Y) +
"\t" + FString::Printf(TEXT("%.3f"), GazeTracker->GetWorldGazeDirection().Direction.Z);
if (UniLog.GetLogStream("GazeTrackingLog"))
{
UniLog.Log(out, "GazeTrackingLog");
}
}
void USFLogObject::SetLoggingLoopsActive(bool LoggingLoopsActive) { void USFLogObject::SetLoggingLoopsActive(bool LoggingLoopsActive) {
bLoggingLoopsActive = LoggingLoopsActive; bLoggingLoopsActive = LoggingLoopsActive;
......
...@@ -34,7 +34,7 @@ void FSFLoggingUtils::SetupLoggingStreams() ...@@ -34,7 +34,7 @@ void FSFLoggingUtils::SetupLoggingStreams()
ILogStream* SFLog = UniLog.NewLogStream("SFLog", "StudyFramework/DebuggingLogs", "SFLog.txt", false); ILogStream* SFLog = UniLog.NewLogStream("SFLog", "StudyFramework/DebuggingLogs", "SFLog.txt", false);
SFLog->SetLogToDefaultLog(true); SFLog->SetLogToDefaultLog(true);
//ParticipantLog, PositionLog are set up in Participant init function //ParticipantLog, PositionLog, GazeTrackingLog are set up in SFParticipant::SetupLoggingStreams() function
ILogStream* SFErrorLog = UniLog.NewLogStream("SFErrorLog", "StudyFramework/DebuggingLogs", "SFLog.txt", false); ILogStream* SFErrorLog = UniLog.NewLogStream("SFErrorLog", "StudyFramework/DebuggingLogs", "SFLog.txt", false);
SFErrorLog->SetLogToDefaultLog(true); SFErrorLog->SetLogToDefaultLog(true);
......
...@@ -131,10 +131,14 @@ void USFGameInstance::RestoreLastParticipantForDebugStart(USFCondition* InStartC ...@@ -131,10 +131,14 @@ void USFGameInstance::RestoreLastParticipantForDebugStart(USFCondition* InStartC
const int ParticipantID = USFParticipant::GetLastParticipantId(); const int ParticipantID = USFParticipant::GetLastParticipantId();
Participant = NewObject<USFParticipant>(this, Participant = NewObject<USFParticipant>(this,
FName(TEXT("Participant_") + FString::FromInt(ParticipantID))); FName(TEXT("Participant_") + FString::FromInt(ParticipantID)));
StudySetup = USFParticipant::GetLastParticipantSetup();
Participant->Initialize(ParticipantID); Participant->Initialize(ParticipantID);
Participant->SetupLoggingStreams(StudySetup->UseGazeTracker != EGazeTrackerMode::NotTracking);
Participant->LoadConditionsFromJson(); Participant->LoadConditionsFromJson();
StudySetup = USFParticipant::GetLastParticipantSetup();
InitFadeHandler(StudySetup->FadeConfig); InitFadeHandler(StudySetup->FadeConfig);
StartCondition = InStartCondition; StartCondition = InStartCondition;
...@@ -240,6 +244,7 @@ void USFGameInstance::PrepareWithStudySetup(ASFStudySetup* Setup) ...@@ -240,6 +244,7 @@ void USFGameInstance::PrepareWithStudySetup(ASFStudySetup* Setup)
Participant = NewObject<USFParticipant>(this, Participant = NewObject<USFParticipant>(this,
FName(TEXT("Participant_") + FString::FromInt(ParticipantID))); FName(TEXT("Participant_") + FString::FromInt(ParticipantID)));
Participant->Initialize(ParticipantID); Participant->Initialize(ParticipantID);
Participant->SetupLoggingStreams(StudySetup->UseGazeTracker != EGazeTrackerMode::NotTracking);
Participant->SetStudyConditions(Conditions); Participant->SetStudyConditions(Conditions);
if (bRecoverParticipantData) if (bRecoverParticipantData)
...@@ -531,7 +536,8 @@ USFParticipant* USFGameInstance::GetParticipant() const ...@@ -531,7 +536,8 @@ USFParticipant* USFGameInstance::GetParticipant() const
bool USFGameInstance::LogTick(float DeltaSeconds) bool USFGameInstance::LogTick(float DeltaSeconds)
{ {
if (LogObject->bLoggingLoopsActive){ if (LogObject->bLoggingLoopsActive){
LogObject->LoggingLoopsLogToFile(); LogObject->WritePositionLogToFile();
LogObject->WriteGazeTrackingLogToFile();
} }
return true; return true;
} }
......
...@@ -18,30 +18,37 @@ USFParticipant::~USFParticipant() ...@@ -18,30 +18,37 @@ USFParticipant::~USFParticipant()
{ {
} }
bool USFParticipant::Initialize(int Participant, bool bSetIdOnly /*= false*/) bool USFParticipant::Initialize(int Participant)
{ {
ParticipantID = Participant; ParticipantID = Participant;
if(bSetIdOnly)
{ StartTime = FPlatformTime::Seconds();
return true; return true;
} }
void USFParticipant::SetupLoggingStreams(bool bGazeTracking)
{
const FString Timestamp = FDateTime::Now().ToString(); const FString Timestamp = FDateTime::Now().ToString();
const FString Filename = "LogParticipant-" + FString::FromInt(ParticipantID) + "_" + Timestamp + ".txt"; const FString Filename = "LogParticipant-" + FString::FromInt(ParticipantID) + "_" + Timestamp + ".txt";
ILogStream* ParticipantLog = UniLog.NewLogStream("ParticipantLog", "StudyFramework/StudyLogs/ParticipantLogs", ILogStream* ParticipantLog = UniLog.NewLogStream("ParticipantLog", "StudyFramework/StudyLogs/ParticipantLogs",
Filename, false); Filename, false);
ILogStream* PositionLog = UniLog.NewLogStream("PositionLog", "StudyFramework/StudyLogs/PositionLogs", ILogStream* PositionLog = UniLog.NewLogStream("PositionLog", "StudyFramework/StudyLogs/PositionLogs",
"Position" + Filename, false); "Position" + Filename, false);
if (bGazeTracking)
{
ILogStream* GazeTrackingLog = UniLog.NewLogStream("GazeTrackingLog", "StudyFramework/StudyLogs/GazeTrackingLogs",
"GazeTracking" + Filename, false);
}
if (USFGameInstance::Get()) if (USFGameInstance::Get())
{ {
USFGameInstance::Get()->GetLogObject()->LogHeaderRows(); USFGameInstance::Get()->GetLogObject()->WritePositionLogHeaderRow();
USFGameInstance::Get()->GetLogObject()->WriteGazeTrackingLogHeaderRow();
} }
else else
{ {
FSFLoggingUtils::Log("GameInstance not set up yet, no header rows are written to participant logs."); FSFLoggingUtils::Log("GameInstance not set up yet, no header rows are written to participant logs.");
} }
return true;
} }
void USFParticipant::SetStudyConditions(TArray<USFCondition*> NewConditions) void USFParticipant::SetStudyConditions(TArray<USFCondition*> NewConditions)
......
...@@ -148,7 +148,7 @@ void ASFStudySetup::GenerateTestStudyRuns() const ...@@ -148,7 +148,7 @@ void ASFStudySetup::GenerateTestStudyRuns() const
{ {
const TArray<USFCondition*> Conditions = GetAllConditionsForRun(ParticipantID); const TArray<USFCondition*> Conditions = GetAllConditionsForRun(ParticipantID);
USFParticipant* TmpParticipant = NewObject<USFParticipant>(); USFParticipant* TmpParticipant = NewObject<USFParticipant>();
TmpParticipant->Initialize(ParticipantID, true); TmpParticipant->Initialize(ParticipantID);
TmpParticipant->SetStudyConditions(Conditions); //this also saves it to json TmpParticipant->SetStudyConditions(Conditions); //this also saves it to json
} }
} }
......
...@@ -30,20 +30,28 @@ class STUDYFRAMEWORKPLUGIN_API USFGazeTracker : public UObject ...@@ -30,20 +30,28 @@ class STUDYFRAMEWORKPLUGIN_API USFGazeTracker : public UObject
public: public:
void Init(EGazeTrackerMode Mode); void Init(EGazeTrackerMode Mode);
//returns pair of Origin and Direction, relative to the head! //returns pair of Origin and Direction, in world coordinates
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
FGazeRay GetGazeDirection(); FGazeRay GetWorldGazeDirection();
//returns the name of the gaze target or empty string if none found or not tracking //returns the name of the gaze target or empty string if none found or not tracking
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
FString GetCurrentGazeTarget(); FString GetCurrentGazeTarget();
//returns pair of Origin and Direction, relative to the head!
UFUNCTION(BlueprintCallable)
FGazeRay GetLocalGazeDirection();
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void LaunchCalibration(); void LaunchCalibration();
//whether currently the eyes are tracked, otherwise head-forward is used (this can change during execution if the HMD is taken off etc.)
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
bool IsTrackingEyes(); bool IsTrackingEyes();
UPROPERTY(BlueprintReadWrite)
bool bDebugRenderRayTraces = false;
private: private:
bool bEyeTrackingStarted = false; bool bEyeTrackingStarted = false;
......
...@@ -61,6 +61,8 @@ public: ...@@ -61,6 +61,8 @@ public:
UFUNCTION() UFUNCTION()
void OnShowConditionsButtonPressed(); void OnShowConditionsButtonPressed();
const ASFHMDSpectatorHUDHelp* GetHUDHelper();
protected: protected:
UPROPERTY(VisibleAnywhere) UPROPERTY(VisibleAnywhere)
USFHUDWidget* HUDWidget; USFHUDWidget* HUDWidget;
......
...@@ -65,7 +65,14 @@ public: ...@@ -65,7 +65,14 @@ public:
UFUNCTION() UFUNCTION()
bool GetLoggingLoopsActive(); bool GetLoggingLoopsActive();
UFUNCTION() UFUNCTION()
static void LogHeaderRows(); static void WritePositionLogHeaderRow();
UFUNCTION() UFUNCTION()
static void LoggingLoopsLogToFile(); static void WritePositionLogToFile();
UFUNCTION()
static void WriteGazeTrackingLogHeaderRow();
UFUNCTION()
static void WriteGazeTrackingLogToFile();
}; };
...@@ -28,7 +28,8 @@ public: ...@@ -28,7 +28,8 @@ public:
USFParticipant(); USFParticipant();
~USFParticipant(); ~USFParticipant();
bool Initialize(int Participant, bool bSetIdOnly = false); bool Initialize(int Participant);
void SetupLoggingStreams(bool bGazeTracking);
void SetStudyConditions(TArray<USFCondition*> NewConditions); void SetStudyConditions(TArray<USFCondition*> NewConditions);
bool StartStudy(); bool StartStudy();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment