Editor/MocapWindow.cs
using System;
using Editor;
using Sandbox;
namespace HumanoidMocapX.EditorTools;
/// <summary>Third-person FlowHMR capture using Humanoid Mocap's preview, rigs and exporter.</summary>
public static partial class MocapWindow
{
public const string Title = "Humanoid Mocap X";
const float LabelWidth = 130;
static Dialog window;
[Event("tools.editorwindow.createview")]
static void Register(Menu menu) => EditorWindow.DockManager.RegisterDockType(new DockManager.DockInfo
{
Title = Title,
Icon = "videocam",
CreateAction = () => { Open(); return null; }
});
[Event("tools.editorwindow.postcreateview")]
static void Configure(Menu menu)
{
var option = menu.GetOption(Title);
if (option is null) return;
option.Toggled = null; option.Checkable = false; option.Triggered = Open;
}
/// <summary>Opens or raises the X workspace. Human is selected initially.</summary>
public static void Open()
{
if (window.IsValid()) { window.Show(); window.Window.Raise(); return; }
var options = new CaptureOptions
{
Model = EditorCookie.Get("HumanoidMocapX.Model", FlowModel.Latest),
CameraInterval = EditorCookie.Get("HumanoidMocapX.CameraInterval", 10),
Lanes = EditorCookie.Get("HumanoidMocapX.Lanes", CaptureLanes.Auto)
};
if (options.Lanes is < CaptureLanes.Auto or > CaptureLanes.Maximum) options.Lanes = CaptureLanes.Auto;
if (options.CameraInterval is not (1 or 10 or 30)) options.CameraInterval = 10;
if (options.Model is not (FlowModel.Base or FlowModel.Latest)) options.Model = FlowModel.Latest;
window = new CaptureDialog(options);
window.Show();
}
/// <summary>The X part of Mocap adjustments: where videos are processed, the motion model and,
/// for this computer, frames at once and model downloads.</summary>
internal static void Settings(Widget host, CaptureOptions options, HostingSettings hosting, SshSettings machine, Action applied, Action modelsChanged)
{
// Where captures run, and the only place to set or change it.
var where = SettingsSection.Add(host, "Processing", openByDefault: true);
where.Layout.Add(new ProcessingSection(where, hosting, machine, applied, LabelWidth));
// Built in Local models below; Base or Latest decides which FlowHMR file it checks.
ModelDownloads list = null;
var section = SettingsSection.Add(host, "Motion model", openByDefault: true);
var models = Row(section, "FlowHMR model", Ui.Framed(new ComboBox(section)));
foreach (var model in new[] { FlowModel.Latest, FlowModel.Base })
models.AddItem(model.ToString(), onSelected: () =>
{
options.Model = model; EditorCookie.Set("HumanoidMocapX.Model", model); modelsChanged(); list?.Refresh();
}, selected: model == options.Model);
var sampling = Row(section, "Camera sampling", Ui.Framed(new ComboBox(section)
{
ToolTip = "Sparser camera sampling uses less GPU memory but can lose accuracy with fast camera motion. Animation stays at 30 fps."
}));
foreach (var (interval, label) in new[] { (1, "Every frame"), (10, "Every 10th frame"), (30, "Every 30th frame") })
sampling.AddItem(label, onSelected: () =>
{
options.CameraInterval = interval; EditorCookie.Set("HumanoidMocapX.CameraInterval", interval);
}, selected: interval == options.CameraInterval);
// The provider and the processing machine use their own GPU and models.
if (hosting.Enabled || machine.Enabled) return;
var capacity = FlowVideoProcessor.LaneCapacity();
var lanes = Row(section, "Parallel frames", Ui.Framed(new ComboBox(section)
{
ToolTip = "Frames processed at once during local capture. More is faster and uses more GPU memory; results are identical. Any choice is capped to what this GPU's free memory allows when capture starts."
}));
for (var count = CaptureLanes.Auto; count <= CaptureLanes.Maximum; count++)
{
var choice = count;
var label = choice == CaptureLanes.Auto ? $"Auto · up to {capacity} on this GPU"
: choice > capacity ? $"{choice} · capped to {capacity} on this GPU" : choice.ToString();
lanes.AddItem(label, onSelected: () =>
{
options.Lanes = choice; EditorCookie.Set("HumanoidMocapX.Lanes", choice);
}, selected: choice == options.Lanes);
}
var downloads = SettingsSection.Add(host, "Local models", openByDefault: false);
list = downloads.Layout.Add(new ModelDownloads(downloads, options, modelsChanged));
}
/// <summary>Adds a labelled row whose control takes the remaining width.</summary>
static T Row<T>(Widget host, string label, T control) where T : Widget
{
var row = host.Layout.AddRow(); row.Spacing = 8;
row.Add(new Label(label, host) { FixedWidth = LabelWidth });
return row.Add(control, 1);
}
}