VR4RoboticArm2/VR4RoboticArm/Library/PackageCache/com.meta.xr.sdk.movement/Runtime/Native/Scripts/Serialization/SequenceFileReader.cs
IonutMocanu 48cccc22ad Main2
2025-09-08 11:13:29 +03:00

248 lines
8.8 KiB
C#

// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
using Meta.XR.Movement.Playback;
using Meta.XR.Movement.Retargeting;
using System;
using System.IO;
using Unity.Collections;
using UnityEngine;
using static Meta.XR.Movement.MSDKUtility;
namespace Meta.XR.Movement.Recording
{
/// <summary>
/// Reads retargeter data from file to apply to a character.
/// </summary>
public class SequenceFileReader : IPlaybackBehaviour
{
/// <inheritdoc cref="IPlaybackBehaviour.SnapshotIndex"/>
public int SnapshotIndex => _playbackManager.SnapshotIndex;
/// <inheritdoc cref="IPlaybackBehaviour.NumSnapshots"/>
public int NumSnapshots => _playbackManager.NumSnapshots;
/// <inheritdoc cref="IPlaybackBehaviour.HasOpenedFileForPlayback"/>
public bool HasOpenedFileForPlayback => _playbackManager.HasActivePlaybackFile;
/// <inheritdoc cref="IRecordingBehaviour.BandwidthKbps"/>
public float BandwidthKbps => _bandwidthRecorder.BandwidthKbps;
private BandwidthRecorder _bandwidthRecorder = new BandwidthRecorder();
/// <inheritdoc cref="IPlaybackBehaviour.UserActivelyScrubbing"/>
public bool UserActivelyScrubbing
{
get => _activelyScrubbing;
set
{
_activelyScrubbing = value;
}
}
/// <summary>
/// Gets the current body pose data from the tracker.
/// </summary>
public NativeArray<NativeTransform> BodyPose => _deserSourcePose;
/// <summary>
/// Gets whether the playback is currently active and not paused.
/// </summary>
public bool IsPlaying => _playbackManager.HasActivePlaybackFile && !_pauseState;
/// <summary>
/// Gets the current playback time in seconds relative to the start time.
/// </summary>
public float PlaybackTime => _playbackManager.NetworkTime - (float)_playbackManager.StartNetworkTime;
private UInt64 _playbackHandle;
private SequencePlaybackManager _playbackManager =
new SequencePlaybackManager();
private NativeArray<NativeTransform> _deserSourcePose;
private SerializationCompressionType _receivedCompressionType;
private int _numSourceJoints;
private int[] _sourceParentIndices;
private bool _activelyScrubbing;
private bool _pauseState = false;
~SequenceFileReader()
{
CleanUp();
}
/// <summary>
/// Initializes the sequence file reader with the specified retargeting handle.
/// </summary>
/// <param name="handle">The retargeting handle to use for playback.</param>
public void Init(UInt64 handle)
{
_playbackHandle = handle;
GetSkeletonInfo(_playbackHandle, SkeletonType.SourceSkeleton, out var sourceSkeletonInfo);
_numSourceJoints = sourceSkeletonInfo.JointCount;
var nativeSourceParentIndices = new NativeArray<int>(_numSourceJoints,
Allocator.Temp, NativeArrayOptions.UninitializedMemory);
GetParentJointIndexesByRef(_playbackHandle, SkeletonType.SourceSkeleton, ref nativeSourceParentIndices);
_sourceParentIndices = nativeSourceParentIndices.ToArray();
}
/// <inheritdoc cref="IPlaybackBehaviour.Seek(int)"/>
public bool Seek(int snapshotIndex)
{
return _playbackManager.Seek(
_playbackHandle,
snapshotIndex,
ProcessSnapshotGetNetworkTimeDelegate,
DeserializeData);
}
/// <inheritdoc cref="IPlaybackBehaviour.PlaybackRecording"/>
public bool PlayBackRecording(string playbackAssetPath)
{
_playbackManager.ClosePlaybackFileIfOpen();
var finalPath = Path.Combine(Application.dataPath, playbackAssetPath);
_playbackManager.OpenFileForPlayback(_playbackHandle, finalPath);
if (_playbackManager.HasActivePlaybackFile)
{
MSDKUtility.ResetInterpolators(_playbackHandle);
_playbackManager.ResetTimestampsToStart();
_pauseState = false;
}
return _playbackManager.HasActivePlaybackFile;
}
/// <inheritdoc cref="IPlaybackBehaviour.SetPauseState(bool)"/>
public void SetPauseState(bool pauseState)
{
_pauseState = pauseState;
}
/// <inheritdoc cref="IPlaybackBehaviour.ClosePlaybackFile"/>
public void ClosePlaybackFile()
{
_playbackManager.ClosePlaybackFileIfOpen();
}
/// <summary>
/// Plays the next frame from the sequence file, updating the pose data.
/// This method handles time progression, looping, and interpolation.
/// </summary>
public void PlayNextFrame()
{
if (_activelyScrubbing)
{
return;
}
if (_pauseState)
{
return;
}
_playbackManager.NetworkTime += Time.deltaTime;
if (_playbackManager.ReadAllSnapshots)
{
_playbackManager.ResetTimestampsToStart();
_playbackManager.RestartSnapshotReading();
ResetInterpolators(_playbackHandle);
}
if (_playbackManager.NetworkTime < _playbackManager.LastTimeStamp && _playbackManager.NetworkTime > 0.0f)
{
return;
}
byte[] snapshotBytes = _playbackManager.ReadNextSnapshotBytes();
if (snapshotBytes != null)
{
float readTimestamp = ProcessSnapshotGetNetworkTimeDelegate(snapshotBytes);
_playbackManager.LastTimeStamp = (float)readTimestamp;
}
}
/// <summary>
/// Processes snapshot bytes and returns the network time.
/// This method is used as a delegate for snapshot processing during playback.
/// </summary>
/// <param name="snapshotBytes">The snapshot bytes to process.</param>
/// <returns>The network time of the processed snapshot.</returns>
public float ProcessSnapshotGetNetworkTimeDelegate(
byte[] snapshotBytes)
{
return _playbackManager.ProcessSnapshotBytesAndGetNetworkTime(
snapshotBytes,
_activelyScrubbing,
DeserializeData,
LerpReceivedData,
ProcessReceivedData);
}
private void LerpReceivedData()
{
GetInterpolatedSkeleton(
_playbackHandle,
SkeletonType.SourceSkeleton,
ref _deserSourcePose,
_playbackManager.NetworkTime);
}
private void ProcessReceivedData()
{
if (CompressionUsesJointLengths(_receivedCompressionType))
{
SkeletonUtilities.ConvertLocalPosesToAbsolute(_sourceParentIndices, _deserSourcePose);
}
}
private bool DeserializeData(byte[] bytes)
{
AllocateDeserializationArrays();
var nativeBytes =
new NativeArray<byte>(bytes.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < bytes.Length; i++)
{
nativeBytes[i] = bytes[i];
}
// Create dummy arguments for deserialized data that we don't care about.
var emptyBody = new NativeArray<NativeTransform>(1, Allocator.Temp);
var emptyFace = new NativeArray<float>(1, Allocator.Temp);
var frameData = new FrameData();
if (!DeserializeSkeletonAndFace(
_playbackHandle,
nativeBytes,
out var timestamp,
out _receivedCompressionType,
out var ack,
ref emptyBody,
ref emptyFace,
ref _deserSourcePose,
ref frameData))
{
Debug.LogError("Data deserialized is invalid!");
return false;
}
return true;
}
private void AllocateDeserializationArrays()
{
if (!_deserSourcePose.IsCreated)
{
_deserSourcePose =
new NativeArray<NativeTransform>(_numSourceJoints, Allocator.Persistent,
NativeArrayOptions.UninitializedMemory);
}
}
private void CleanUp()
{
if (_deserSourcePose.IsCreated)
{
_deserSourcePose.Dispose();
}
_playbackManager.ClosePlaybackFileIfOpen();
}
}
}