| 970 | } |
| 971 | |
| 972 | void AMjReplayManager::OnReplayStep(mjModel* m, mjData* d) |
| 973 | { |
| 974 | if (!bIsReplaying || !m || !d) |
| 975 | return; |
| 976 | |
| 977 | TArray<FMjReplayFrame>& Frames = GetActiveFrames(); |
| 978 | if (Frames.Num() == 0) |
| 979 | return; |
| 980 | |
| 981 | // Advance playback time on the physics thread (no race condition) |
| 982 | double PhysDt = m->opt.timestep; |
| 983 | double CurrentPT = PhysicsPlaybackTime.load(std::memory_order_relaxed); |
| 984 | if (Manager->PhysicsEngine && !Manager->PhysicsEngine->bIsPaused) |
| 985 | { |
| 986 | CurrentPT += PhysDt; |
| 987 | double TotalDuration = Frames.Last().Timestamp - Frames[0].Timestamp; |
| 988 | if (TotalDuration > 0 && CurrentPT > TotalDuration) |
| 989 | { |
| 990 | CurrentPT = 0.0; // Loop |
| 991 | } |
| 992 | PhysicsPlaybackTime.store(CurrentPT, std::memory_order_relaxed); |
| 993 | } |
| 994 | |
| 995 | double StartTime = Frames[0].Timestamp; |
| 996 | double TargetTime = StartTime + CurrentPT; |
| 997 | |
| 998 | int32 FoundIdx = Algo::LowerBound(Frames, TargetTime, |
| 999 | [](const FMjReplayFrame& Frame, double T) { return Frame.Timestamp < T; }); |
| 1000 | if (FoundIdx > 0) |
| 1001 | --FoundIdx; |
| 1002 | FoundIdx = FMath::Clamp(FoundIdx, 0, Frames.Num() - 1); |
| 1003 | |
| 1004 | // Compute interpolation alpha between FoundIdx and FoundIdx+1 |
| 1005 | int32 NextIdx = FMath::Min(FoundIdx + 1, Frames.Num() - 1); |
| 1006 | double Alpha = 0.0; |
| 1007 | if (bInterpolateFrames && NextIdx > FoundIdx) |
| 1008 | { |
| 1009 | double T0 = Frames[FoundIdx].Timestamp; |
| 1010 | double T1 = Frames[NextIdx].Timestamp; |
| 1011 | double Span = T1 - T0; |
| 1012 | if (Span > 1e-9) |
| 1013 | { |
| 1014 | Alpha = FMath::Clamp((TargetTime - T0) / Span, 0.0, 1.0); |
| 1015 | } |
| 1016 | } |
| 1017 | |
| 1018 | const FMjReplayFrame& Frame = Frames[FoundIdx]; |
| 1019 | |
| 1020 | bool bFirstFrame = bFirstReplayFrame; |
| 1021 | if (d->time - LastReplayLogTime > 1.0) |
| 1022 | { |
| 1023 | UE_LOG(LogURLabReplay, Log, TEXT("ReplayManager: Playing '%s' Frame %f (PlaybackTime: %f). Index %d/%d"), |
| 1024 | *ActiveSessionName, Frame.Timestamp, PlaybackTime, FoundIdx, Frames.Num()); |
| 1025 | LastReplayLogTime = d->time; |
| 1026 | } |
| 1027 | |
| 1028 | int32 MatchedJoints = 0; |
| 1029 | int32 UnmatchedJoints = 0; |
no test coverage detected