| 876 | // --- Physics Thread Callbacks --- |
| 877 | |
| 878 | void AMjReplayManager::OnPostStep(mjModel* m, mjData* d) |
| 879 | { |
| 880 | if (!bIsRecording || !m || !d || !d->qpos || !d->qvel) |
| 881 | return; |
| 882 | |
| 883 | // One-shot log per recording session so we can confirm at runtime |
| 884 | // whether the OnPostStep hook is actually firing. Reset in |
| 885 | // StartRecording. |
| 886 | if (!bFirstFrameLogged) |
| 887 | { |
| 888 | bFirstFrameLogged = true; |
| 889 | UE_LOG(LogURLabReplay, Log, |
| 890 | TEXT("ReplayManager::OnPostStep first frame: nq=%d nv=%d time=%.4f"), |
| 891 | m->nq, m->nv, d->time); |
| 892 | } |
| 893 | |
| 894 | if (!bCacheValid || CachedJointNames.Num() != m->njnt) |
| 895 | { |
| 896 | UpdateCache(m); |
| 897 | } |
| 898 | |
| 899 | FMjReplayFrame Frame; |
| 900 | Frame.Timestamp = d->time; |
| 901 | |
| 902 | for (int i = 0; i < m->njnt; ++i) |
| 903 | { |
| 904 | FString Name = CachedJointNames[i]; |
| 905 | if (Name.IsEmpty()) |
| 906 | continue; |
| 907 | |
| 908 | int qpos_adr = m->jnt_qposadr[i]; |
| 909 | int qvel_adr = m->jnt_dofadr[i]; |
| 910 | |
| 911 | int qpos_dim = 0; |
| 912 | int qvel_dim = 0; |
| 913 | |
| 914 | int jnt_type = m->jnt_type[i]; |
| 915 | if (jnt_type == mjJNT_FREE) |
| 916 | { |
| 917 | qpos_dim = 7; |
| 918 | qvel_dim = 6; |
| 919 | } |
| 920 | else if (jnt_type == mjJNT_BALL) |
| 921 | { |
| 922 | qpos_dim = 4; |
| 923 | qvel_dim = 3; |
| 924 | } |
| 925 | else if (jnt_type == mjJNT_SLIDE) |
| 926 | { |
| 927 | qpos_dim = 1; |
| 928 | qvel_dim = 1; |
| 929 | } |
| 930 | else if (jnt_type == mjJNT_HINGE) |
| 931 | { |
| 932 | qpos_dim = 1; |
| 933 | qvel_dim = 1; |
| 934 | } |
| 935 |
no test coverage detected