200 lines
7.1 KiB
C#
200 lines
7.1 KiB
C#
#nullable enable
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Converters;
|
|
using Newtonsoft.Json.Linq;
|
|
using T3.Core.Video;
|
|
using T3.Editor.Gui.UiHelpers;
|
|
using T3.Editor.UiModel;
|
|
using T3.Editor.UiModel.ProjectHandling;
|
|
|
|
namespace T3.Editor.Gui.Windows.RenderExport;
|
|
|
|
internal sealed class RenderSettings
|
|
{
|
|
public static readonly RenderSettings Defaults = new();
|
|
|
|
/// <summary>
|
|
/// Returns the render settings for the focused composition, initializing from defaults
|
|
/// (with legacy path migration) if none exist yet.
|
|
/// </summary>
|
|
public static RenderSettings Current
|
|
{
|
|
get
|
|
{
|
|
var symbolUi = ProjectView.Focused?.CompositionInstance?.Symbol.GetSymbolUi();
|
|
if (symbolUi == null)
|
|
return Defaults;
|
|
|
|
if (symbolUi.RenderSettings != null)
|
|
return symbolUi.RenderSettings;
|
|
|
|
// First access for this composition — initialize with defaults + legacy paths
|
|
var settings = new RenderSettings();
|
|
|
|
#pragma warning disable CS0612, CS0618 // Obsolete
|
|
var legacy = UserSettings.Config;
|
|
if (!string.IsNullOrEmpty(legacy.RenderVideoFilePath))
|
|
settings.VideoFilePath = legacy.RenderVideoFilePath;
|
|
if (!string.IsNullOrEmpty(legacy.RenderSequenceFilePath))
|
|
settings.SequenceFilePath = legacy.RenderSequenceFilePath;
|
|
if (!string.IsNullOrEmpty(legacy.RenderSequenceFileName))
|
|
settings.SequenceFileName = legacy.RenderSequenceFileName;
|
|
if (!string.IsNullOrEmpty(legacy.RenderSequencePrefix))
|
|
settings.SequencePrefix = legacy.RenderSequencePrefix;
|
|
#pragma warning restore CS0612, CS0618
|
|
|
|
// Don't flag as modified here: this getter runs from mere UI reads (e.g. the output window's
|
|
// render tooltip), which must not dirty the composition. Actual changes to the settings are
|
|
// flagged by the render window and render process.
|
|
symbolUi.RenderSettings = settings;
|
|
return settings;
|
|
}
|
|
}
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public TimeReferences TimeReference;
|
|
public float StartInBars;
|
|
public float EndInBars = 8f;
|
|
public float FrameRate = 60f;
|
|
public int OverrideMotionBlurSamples = -1;
|
|
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public RenderModes RenderMode = RenderModes.Video;
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public VideoExportCodec VideoCodec = VideoExportCodec.H264;
|
|
public int Bitrate = 25_000_000;
|
|
public bool AutoIncrementVersionNumber = true;
|
|
public bool CreateSubFolder = true;
|
|
public bool AutoIncrementSubFolder = true;
|
|
public bool ExportAudio = true;
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public ScreenshotWriter.FileFormats FileFormat;
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public TimeRanges TimeRange = TimeRanges.Custom;
|
|
|
|
// Continuous-capture options — only meaningful when TimeRange == Continuous.
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public ContinuousCaptureClock ContinuousClock = ContinuousCaptureClock.Realtime;
|
|
[JsonConverter(typeof(StringEnumConverter))]
|
|
public ContinuousFrameRateMode ContinuousFrameRate = ContinuousFrameRateMode.FixedFps;
|
|
|
|
public float ResolutionFactor = 1f;
|
|
|
|
// Render paths — persisted per-project in .t3ui
|
|
public string VideoFilePath = "./Render/render-v01.mp4";
|
|
public string SequenceFilePath = "./ImageSequence/";
|
|
public string SequenceFileName = "v01";
|
|
public string SequencePrefix = "render";
|
|
|
|
public void CopyFrom(RenderSettings other)
|
|
{
|
|
TimeReference = other.TimeReference;
|
|
StartInBars = other.StartInBars;
|
|
EndInBars = other.EndInBars;
|
|
FrameRate = other.FrameRate;
|
|
OverrideMotionBlurSamples = other.OverrideMotionBlurSamples;
|
|
RenderMode = other.RenderMode;
|
|
VideoCodec = other.VideoCodec;
|
|
Bitrate = other.Bitrate;
|
|
AutoIncrementVersionNumber = other.AutoIncrementVersionNumber;
|
|
CreateSubFolder = other.CreateSubFolder;
|
|
AutoIncrementSubFolder = other.AutoIncrementSubFolder;
|
|
ExportAudio = other.ExportAudio;
|
|
FileFormat = other.FileFormat;
|
|
TimeRange = other.TimeRange;
|
|
ContinuousClock = other.ContinuousClock;
|
|
ContinuousFrameRate = other.ContinuousFrameRate;
|
|
ResolutionFactor = other.ResolutionFactor;
|
|
VideoFilePath = other.VideoFilePath;
|
|
SequenceFilePath = other.SequenceFilePath;
|
|
SequenceFileName = other.SequenceFileName;
|
|
SequencePrefix = other.SequencePrefix;
|
|
}
|
|
|
|
public RenderSettings Clone()
|
|
{
|
|
var clone = new RenderSettings();
|
|
clone.CopyFrom(this);
|
|
return clone;
|
|
}
|
|
|
|
#region Serialization
|
|
|
|
internal void WriteToJson(JsonTextWriter writer)
|
|
{
|
|
writer.WritePropertyName("RenderExport");
|
|
writer.WriteRawValue(JsonConvert.SerializeObject(this, Formatting.Indented));
|
|
}
|
|
|
|
internal static RenderSettings? ReadFromJson(JToken parentToken)
|
|
{
|
|
var token = parentToken["RenderExport"];
|
|
if (token == null)
|
|
return null;
|
|
|
|
return token.ToObject<RenderSettings>();
|
|
}
|
|
|
|
#endregion
|
|
|
|
internal enum RenderModes
|
|
{
|
|
Video,
|
|
ImageSequence
|
|
}
|
|
|
|
internal enum TimeReferences
|
|
{
|
|
Bars,
|
|
Seconds,
|
|
Frames
|
|
}
|
|
|
|
internal enum TimeRanges
|
|
{
|
|
Custom,
|
|
Loop,
|
|
Soundtrack,
|
|
|
|
/// <summary>Open-ended recording: capture starts immediately and runs until the user stops it, with no
|
|
/// predetermined end. See <see cref="ContinuousClock"/> for how playback time is driven.</summary>
|
|
Continuous,
|
|
}
|
|
|
|
/// <summary>What drives playback time during a <see cref="TimeRanges.Continuous"/> capture.</summary>
|
|
internal enum ContinuousCaptureClock
|
|
{
|
|
/// <summary>Leave playback under live/user control and grab whatever the output shows each frame
|
|
/// (OBS/VJ-style). Best for live performance and audio-reactive/interactive content.</summary>
|
|
Realtime,
|
|
|
|
/// <summary>Step playback forward at the target FPS like a normal render, but without a fixed end —
|
|
/// frame-perfect even when encoding is slower than realtime.</summary>
|
|
Deterministic,
|
|
}
|
|
|
|
/// <summary>The frame-rate written for a <see cref="TimeRanges.Continuous"/> capture.</summary>
|
|
internal enum ContinuousFrameRateMode
|
|
{
|
|
/// <summary>Write at the settings' FPS; in realtime grab, pace via a wall-clock accumulator.</summary>
|
|
FixedFps,
|
|
|
|
/// <summary>Variable frame rate timestamped by wall-clock. Reserved — not yet implemented.</summary>
|
|
Variable,
|
|
}
|
|
|
|
internal readonly struct QualityLevel
|
|
{
|
|
internal QualityLevel(double bits, string title, string description)
|
|
{
|
|
MinBitsPerPixelSecond = bits;
|
|
Title = title;
|
|
Description = description;
|
|
}
|
|
|
|
internal readonly double MinBitsPerPixelSecond;
|
|
internal readonly string Title;
|
|
internal readonly string Description;
|
|
}
|
|
}
|