Data container representing the result of retargeting a single source take into one output clip. It stores metadata (clip name, source file/id), success/error state, generated DMX text and solved per-frame transforms, sampling and flag fields, footstep events and variant names, and provides methods to override DMX content and release large payloads.
#nullable enable annotations
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeter.Core.Mapping;
using HumanoidRetargeter.Core.Maths;
using HumanoidRetargeter.Core.Target;
using HumanoidRetargeter.Core.Skeleton;
namespace HumanoidRetargeter.Core;
/// <summary>Outcome of retargeting one source take to one output clip.</summary>
public sealed class ClipResult
{
/// <summary>Final (collision-suffixed) sequence name.</summary>
public required string ClipName { get; init; }
/// <summary>The file the request came from (as supplied on the request).</summary>
public required string SourceFileName { get; init; }
/// <summary>
/// Identity of the originating request (<see cref="RetargetRequest.SourceId"/>, falling
/// back to <see cref="RetargetRequest.SourceFileName"/>). Callers join clip results back
/// to their own entries on this — file NAMES may collide across folders.
/// </summary>
public string SourceId { get; init; } = "";
/// <summary>Sanitized DMX file name (<c><clip>.dmx</c>); empty on failure.</summary>
public string DmxFileName { get; init; } = "";
/// <summary>Full DMX text (keyvalues2) of the retargeted clip; empty on failure or after
/// <see cref="ReleaseHeavyData"/>.</summary>
public string DmxContent { get; internal set; } = "";
/// <summary>Replaces the serialized DMX (diagnostic harnesses only: the UI smoke
/// gate's bone-bisect re-serializes mutated frames so the COMPILE reflects them).</summary>
public void OverrideDmxContent(string dmx) => DmxContent = dmx ?? "";
/// <summary>Mapping report of the source file; null when the failure happened before
/// detection (unreadable file).</summary>
public MappingReportInfo? Mapping { get; init; }
/// <summary>Whether this clip converted successfully.</summary>
public required bool Success { get; init; }
/// <summary>Failure description when <see cref="Success"/> is false.</summary>
public string? Error { get; init; }
/// <summary>
/// The final solved frames (after cleanup and IK baking) — one local
/// <see cref="XForm"/> per TARGET rig bone, in TARGET skeleton bone order, per frame.
/// Retained for the UI preview path (<c>Model.Builder.AddFrame</c> consumes exactly
/// this layout). Null on failure or after <see cref="ReleaseHeavyData"/>.
/// </summary>
public List<XForm[]>? SolvedFrames { get; internal set; }
/// <summary>
/// Drops the heavy per-clip payloads (<see cref="DmxContent"/>, <see cref="SolvedFrames"/>)
/// once the caller has written them to disk — UI windows keep clip results around for
/// status display, which must not pin megabytes of frame data indefinitely. Previews
/// re-solve on demand (subsecond), so nothing else needs the frames after the write.
/// </summary>
public void ReleaseHeavyData()
{
DmxContent = "";
SolvedFrames = null;
}
/// <summary>Output sample rate.</summary>
public float Fps { get; init; }
/// <summary>Looping flag of the output sequence (source flag or request override).</summary>
public bool Looping { get; init; }
/// <summary>Whether the clip's vmdl AnimFile entry carries an ExtractMotion node
/// (request had <c>RootMotion == Extract</c>).</summary>
public bool ExtractMotion { get; init; }
/// <summary>
/// Generated <c>AE_FOOTSTEP</c> events for this clip, in frame order — the same list
/// emitted as AnimEvent children on the clip's vmdl AnimFile entry. Empty unless the
/// request set <see cref="RetargetRequest.GenerateFootstepEvents"/> (and detection found
/// touchdowns).
/// </summary>
public IReadOnlyList<AnimEventEntry> FootstepEvents { get; init; } = Array.Empty<AnimEventEntry>();
/// <summary>
/// True when this clip is the mirrored twin produced by
/// <see cref="RetargetRequest.CreateMirroredVariant"/> (named <c><clip>_M</c>,
/// collision-suffixed as usual).
/// </summary>
public bool IsMirroredVariant { get; init; }
/// <summary>
/// True when <see cref="RetargetRequest.CreateAdditiveVariant"/> registered a companion
/// additive (<c>_delta</c>) AnimFile entry for this clip in the generated/augmented vmdl
/// (an AnimSubtract sequence reusing this clip's DMX — no separate clip result exists,
/// since no separate DMX is produced).
/// </summary>
public bool HasAdditiveVariant { get; init; }
/// <summary>Name of the additive variant sequence (<c><clip>_delta</c>,
/// collision-suffixed); null when <see cref="HasAdditiveVariant"/> is false.</summary>
public string? AdditiveVariantName { get; init; }
}