Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,26 @@

#include "Hdf5SimulationReader.h"
#include "Misc/Paths.h"
#include "Misc/ScopeLock.h"

DEFINE_LOG_CATEGORY_STATIC(LogHdf5SimulationReader, Log, All);

namespace
{
/**
* The bundled HDF5 library is built without thread safety (Threadsafety: OFF in
* libhdf5.settings), so no two threads may execute HDF5 code concurrently.
* Geometry and trajectory loading both read the same .h5 from different threads,
* so every public reader method serializes on this process-wide lock.
* The lock is recursive on all UE platforms, so public methods may call each other.
*/
FCriticalSection& GetHdf5LibraryLock()
{
static FCriticalSection Hdf5LibraryLock;
return Hdf5LibraryLock;
}
}

FHdf5SimulationReader::FHdf5SimulationReader()
{
}
Expand All @@ -37,6 +54,8 @@ FHdf5SimulationReader::~FHdf5SimulationReader()

bool FHdf5SimulationReader::OpenFile(const FString& FilePath)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

// Close any previously open file
CloseFile();
Comment on lines +57 to 60

Expand All @@ -58,7 +77,8 @@ bool FHdf5SimulationReader::OpenFile(const FString& FilePath)
if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("Failed to open HDF5 file: %s"), *FilePath);
H5close();
// Do not call H5close() here: it tears down the HDF5 library process-wide,
// crashing any other reader that is mid-operation.
return false;
}

Expand Down Expand Up @@ -111,10 +131,13 @@ bool FHdf5SimulationReader::OpenFile(const FString& FilePath)

void FHdf5SimulationReader::CloseFile()
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId >= 0)
{
H5Fclose(FileId);
H5close();
// Deliberately no H5close(): that shuts down the HDF5 library for the whole
// process, not just this file, and other readers may still be active.
FileId = -1;
DetectedFormat = EHdf5FormatType::Unknown;
TimestepCount = 0;
Expand Down Expand Up @@ -276,6 +299,8 @@ bool FHdf5SimulationReader::ReadStringAttribute(hid_t GroupId, const char* AttrN

bool FHdf5SimulationReader::ReadMetadata(FHdf5SimulationMetadata& OutMetadata)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -320,6 +345,8 @@ bool FHdf5SimulationReader::ReadMetadata(FHdf5SimulationMetadata& OutMetadata)

bool FHdf5SimulationReader::ReadEntities(TArray<FHdf5EntityData>& OutEntities)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -417,6 +444,8 @@ bool FHdf5SimulationReader::ReadEntities(TArray<FHdf5EntityData>& OutEntities)

bool FHdf5SimulationReader::ReadTimesteps(TArray<float>& OutTimesteps)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -453,6 +482,8 @@ bool FHdf5SimulationReader::ReadTimesteps(TArray<float>& OutTimesteps)

bool FHdf5SimulationReader::ReadSamplesPerTimestep(TArray<int32>& OutSamplesPerTimestep)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -496,6 +527,8 @@ bool FHdf5SimulationReader::ReadSamplesPerTimestep(TArray<int32>& OutSamplesPerT

bool FHdf5SimulationReader::ReadAllSamples(TArray<FHdf5SampleData>& OutSamples, bool* OutHasRotationField, bool* OutHasSpeedField)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -608,6 +641,8 @@ bool FHdf5SimulationReader::ReadAllSamples(TArray<FHdf5SampleData>& OutSamples,

bool FHdf5SimulationReader::ReadSamplesForTimestepRange(int32 StartTimestep, int32 EndTimestep, TArray<FHdf5SampleData>& OutSamples)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -718,6 +753,8 @@ EHdf5FormatType FHdf5SimulationReader::DetectFormat(const FString& FilePath)
return EHdf5FormatType::Unknown;
}

FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

FTCHARToUTF8 FilePathUtf8(*FilePath);

// Check if it's a valid HDF5 file
Expand All @@ -731,7 +768,6 @@ EHdf5FormatType FHdf5SimulationReader::DetectFormat(const FString& FilePath)
hid_t TempFileId = H5Fopen(FilePathUtf8.Get(), H5F_ACC_RDONLY, H5P_DEFAULT);
if (TempFileId < 0)
{
H5close();
return EHdf5FormatType::Unknown;
}

Expand All @@ -750,7 +786,6 @@ EHdf5FormatType FHdf5SimulationReader::DetectFormat(const FString& FilePath)
}

H5Fclose(TempFileId);
H5close();

return Result;
}
Expand All @@ -759,6 +794,8 @@ EHdf5FormatType FHdf5SimulationReader::DetectFormat(const FString& FilePath)

bool FHdf5SimulationReader::ReadJuelichMetadata(FHdf5JuelichMetadata& OutMetadata)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -845,6 +882,8 @@ bool FHdf5SimulationReader::ReadJuelichMetadata(FHdf5JuelichMetadata& OutMetadat

bool FHdf5SimulationReader::ReadJuelichTrajectories(TArray<FHdf5JuelichTrajectoryRecord>& OutRecords)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down Expand Up @@ -924,6 +963,8 @@ bool FHdf5SimulationReader::ReadJuelichTrajectories(TArray<FHdf5JuelichTrajector

bool FHdf5SimulationReader::ReadWktGeometry(FString& OutWktGeometry)
{
FScopeLock Hdf5Guard(&GetHdf5LibraryLock());

if (FileId < 0)
{
UE_LOG(LogHdf5SimulationReader, Error, TEXT("No file is open"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,48 @@
#include "Interfaces/ProjectMobiusInterface.h"
#include "Engine/GameInstance.h"
#include "GameInstances/ProjectMobiusGameInstance.h"
#include "Hdf5SimulationReader.h"
#include "Misc/Paths.h"

// Add default functionality here for any IProjectMobiusInterface functions that are not pure virtual.

namespace
{
/**
* HDF5 simulation files can carry the scene geometry alongside the trajectories in the
* root "wkt_geometry" attribute. When such a file is selected as the pedestrian data
* source, adopt it as the geometry source too so a single selection loads both.
* @param GameInst - Mobius game instance to update
* @param DataPath - Full path of the newly selected pedestrian data file
*/
void AdoptEmbeddedGeometrySource(UProjectMobiusGameInstance* GameInst, const FString& DataPath)
{
if (!GameInst || !DataPath.EndsWith(TEXT(".h5"), ESearchCase::IgnoreCase))
{
return;
}

FHdf5SimulationReader Reader;
if (!Reader.OpenFile(DataPath))
{
return;
}

FString WktGeometry;
const bool bHasGeometry = Reader.ReadWktGeometry(WktGeometry) && !WktGeometry.IsEmpty();
Reader.CloseFile();
Comment on lines +55 to +57

if (!bHasGeometry)
{
UE_LOG(LogTemp, Log, TEXT("No embedded geometry in %s, geometry selection left unchanged"), *DataPath);
return;
}

GameInst->SetSimulationMeshFilePath(DataPath);
GameInst->SetSimulationMeshFileName(FPaths::GetCleanFilename(DataPath));
}
}

UProjectMobiusGameInstance* IProjectMobiusInterface::GetMobiusGameInstance(UWorld* World)
{
if(!World)
Expand Down Expand Up @@ -111,6 +150,9 @@ void IProjectMobiusInterface::UpdateMobiusGameInstancePedestrianData(UWorld* Wor

// Set the pedestrian data file name
MobiusGameInst->SetPedestrianDataFileName(FPaths::GetCleanFilename(CompleteDataPath));

// HDF5 files can embed the geometry, load it from the same file
AdoptEmbeddedGeometrySource(MobiusGameInst, CompleteDataPath);
}

void IProjectMobiusInterface::GetMobiusGameInstanceMeshDataFile(UWorld* World, FString& OutCompleteDataPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ void ULoadAgentDataWidget::DialogClosed(const FString& AgentFilePath, const FStr
Feedback->ReportError(
FText::FromString("Invalid Agent Data File"),
FText::FromString("Unsupported agent data file type selected."),
FText::FromString("Supported types: .json"),
FText::FromString("Supported types: .json, .h5"),
FText::FromString("Load Agent Data"));
}
UE_LOG(LogTemp, Warning, TEXT("The file dialog was canceled or an error occurred"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,33 @@
#include "UI/LoadSave/LoadMeshWidget.h"
#include "Subsystems/NativeFileDialogSubsystem.h"
#include "Subsystems/MobiusUserFeedbackSubsystem.h"
#include "GameInstances/ProjectMobiusGameInstance.h"

void ULoadMeshWidget::NativeConstruct()
{
Super::NativeConstruct();

if (UProjectMobiusGameInstance* MobiusGameInst = IProjectMobiusInterface::GetMobiusGameInstance(GetWorld()))
{
MobiusGameInst->OnMeshFileChanged.AddUniqueDynamic(this, &ULoadMeshWidget::OnGameInstanceMeshFileChanged);
}
}

void ULoadMeshWidget::NativeDestruct()
{
if (UProjectMobiusGameInstance* MobiusGameInst = IProjectMobiusInterface::GetMobiusGameInstance(GetWorld()))
{
MobiusGameInst->OnMeshFileChanged.RemoveDynamic(this, &ULoadMeshWidget::OnGameInstanceMeshFileChanged);
}

Super::NativeDestruct();
}

void ULoadMeshWidget::OnGameInstanceMeshFileChanged()
{
// Pull the new path out of the game instance and refresh the text block
GetMobiusGameInstanceData();
UpdateFileTextBlockTexts();
}

void ULoadMeshWidget::OnSelectFileButtonClicked()
Expand Down Expand Up @@ -132,7 +154,7 @@ void ULoadMeshWidget::DialogClosed(const FString& AgentFilePath, const FString&
Feedback->ReportError(
FText::FromString("Invalid Mesh File"),
FText::FromString("Unsupported mesh file type selected."),
FText::FromString("Supported types: .fbx, .obj, .udatasmith, .ifc, .wkt"),
FText::FromString("Supported types: .fbx, .obj, .udatasmith, .ifc, .wkt, .h5"),
FText::FromString("Load Mesh"));
}
UE_LOG(LogTemp, Warning, TEXT("The file dialog was canceled or an error occurred"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class MOBIUSWIDGETS_API ULoadMeshWidget : public ULoadDataParentWidget
#pragma region PUBLIC_METHODS
// Constructor
virtual void NativeConstruct() override;

virtual void NativeDestruct() override;

/**
* Method to call when the SelectFileButton is clicked
Expand All @@ -66,6 +68,13 @@ class MOBIUSWIDGETS_API ULoadMeshWidget : public ULoadDataParentWidget
/** Handler for file dialog errors. Displays error popup to user. */
UFUNCTION()
void OnDialogError(const FString& ErrorTitle, const FString& ErrorMessage);

/**
* Refresh the displayed path when the geometry file is changed elsewhere, e.g. when an
* HDF5 pedestrian data file supplies its own embedded geometry.
*/
UFUNCTION()
void OnGameInstanceMeshFileChanged();
#pragma endregion PUBLIC_METHODS

#pragma endregion METHODS
Expand Down
Loading