| 480 | // --- CSV Import --- |
| 481 | |
| 482 | bool AMjReplayManager::LoadFromCSV(FString FilePath, float Timestep) |
| 483 | { |
| 484 | TArray<FString> Lines; |
| 485 | if (!FFileHelper::LoadFileToStringArray(Lines, *FilePath)) |
| 486 | { |
| 487 | UE_LOG(LogURLabReplay, Error, TEXT("ReplayManager: Failed to load CSV file: %s"), *FilePath); |
| 488 | return false; |
| 489 | } |
| 490 | |
| 491 | if (Lines.Num() < 2) |
| 492 | { |
| 493 | UE_LOG(LogURLabReplay, Error, TEXT("ReplayManager: CSV file has no data rows: %s"), *FilePath); |
| 494 | return false; |
| 495 | } |
| 496 | |
| 497 | // Parse header |
| 498 | TArray<FString> Headers; |
| 499 | Lines[0].ParseIntoArray(Headers, TEXT(","), false); |
| 500 | for (FString& H : Headers) |
| 501 | H.TrimStartAndEndInline(); |
| 502 | |
| 503 | // Find time column |
| 504 | int32 TimeCol = INDEX_NONE; |
| 505 | for (int32 i = 0; i < Headers.Num(); ++i) |
| 506 | { |
| 507 | if (Headers[i].Equals(TEXT("time"), ESearchCase::IgnoreCase) || Headers[i].Equals(TEXT("timestamp"), ESearchCase::IgnoreCase)) |
| 508 | { |
| 509 | TimeCol = i; |
| 510 | break; |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | // group columns by joint name |
| 515 | // Detect bracket notation: "name[N]" -> group under "name" |
| 516 | // Everything else is a 1-DOF joint column |
| 517 | struct FColumnGroup |
| 518 | { |
| 519 | FString JointName; |
| 520 | TArray<int32> ColumnIndices; // Ordered by bracket index or appearance order |
| 521 | }; |
| 522 | TMap<FString, FColumnGroup> JointColumnMap; |
| 523 | |
| 524 | // Camera column indices |
| 525 | int32 CamXCol = INDEX_NONE, CamYCol = INDEX_NONE, CamZCol = INDEX_NONE; |
| 526 | int32 CamPitchCol = INDEX_NONE, CamYawCol = INDEX_NONE, CamRollCol = INDEX_NONE; |
| 527 | |
| 528 | for (int32 i = 0; i < Headers.Num(); ++i) |
| 529 | { |
| 530 | if (i == TimeCol) |
| 531 | continue; |
| 532 | |
| 533 | FString ColName = Headers[i]; |
| 534 | |
| 535 | // Camera columns |
| 536 | if (ColName.Equals(TEXT("cam_x"), ESearchCase::IgnoreCase)) |
| 537 | { |
| 538 | CamXCol = i; |
| 539 | continue; |