Files
ADOFAIRenderer/Data/RenderConfig.cs
T

265 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
namespace ADOFAIRenderer.Data;
/// <summary>
/// Settings of one render: resolution, frame rates, encoding and advanced options. Serialized to JSON in the preset
/// files (PresetManager). <see cref="ADOFAIRenderer.API.RenderSession.Start"/> renders with a copy (<see cref="Clone"/>).
/// </summary>
[SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")]
public class RenderConfig
{
/// <summary>
/// Game frames simulated per second of render time (the game clock advances 1 / RenderFPS seconds per frame).
/// Values above <see cref="VideoFPS"/> render extra frames between video frames, which are not captured
/// (the game state then advances in smaller time steps).
/// </summary>
public double RenderFPS { get; set; }
/// <summary>
/// Frame rate of the output video. The effective rate is min(VideoFPS, RenderFPS).
/// </summary>
public double VideoFPS { get; set; }
/// <summary>Width of the rendered frames and the output video in pixels (also reported by the Screen.width patch).</summary>
public int RenderWidth { get; set; }
/// <summary>Height of the rendered frames and the output video in pixels (also reported by the Screen.height patch).</summary>
public int RenderHeight { get; set; }
/// <summary>Video encoder settings.</summary>
public VideoEncodingConfig VideoConfig { get; set; } = new();
/// <summary>Audio rendering and encoder settings.</summary>
public AudioEncodingConfig AudioConfig { get; set; } = new();
/// <summary>Advanced options (random seed, spectrum processing, tile hiding, container, planet colors).</summary>
public AdvancedConfig AdvancedConfig { get; set; } = new();
/// <summary>Seconds of render time recorded after the end condition first holds (default 15).</summary>
public double DelayAfterEnd { get; set; } = 15;
/// <summary>Whether the judgement texts (hit accuracy texts) are shown during the render (scrHitTextManagerPatch). Default false.</summary>
public bool ShowJudgementText { get; set; } = false;
/// <summary>
/// Copies every value of <paramref name="source"/> into this object; the nested configs are copied value by value
/// into the existing nested objects. Does nothing when <paramref name="source"/> is null.
/// </summary>
public void CopyFrom(RenderConfig? source)
{
if (source == null) return;
RenderFPS = source.RenderFPS;
VideoFPS = source.VideoFPS;
RenderWidth = source.RenderWidth;
RenderHeight = source.RenderHeight;
DelayAfterEnd = source.DelayAfterEnd;
ShowJudgementText = source.ShowJudgementText;
VideoConfig.CopyFrom(source.VideoConfig);
AudioConfig.CopyFrom(source.AudioConfig);
AdvancedConfig.CopyFrom(source.AdvancedConfig);
}
/// <summary>Returns a deep copy (nested configs are new objects).</summary>
public RenderConfig Clone()
{
var clone = new RenderConfig();
clone.CopyFrom(this);
return clone;
}
}
/// <summary>Advanced render options.</summary>
[SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")]
public class AdvancedConfig
{
/// <summary>
/// Seed for UnityEngine.Random set before the game scene loads, so random visual effects repeat between renders;
/// null leaves the random state unchanged.
/// </summary>
public int? RandomSeed { get; set; } = null;
/// <summary>
/// When true, AudioSource.GetSpectrumData is answered from the offline song audio at the render time
/// (AudioSpectrumGenerator), so audio-reactive visuals follow the rendered audio. Default true.
/// </summary>
public bool ProcessAudio { get; set; } = true;
/// <summary>
/// When true, the level's floor tiles (including hold meshes and the portal particles of the level's floors) and the
/// planets are hidden during the render, so only the background and decorations remain (scnGamePatch, scrFloorPatch,
/// RenderManager.OnLastUpdate). Default false.
/// </summary>
public bool NonTile { get; set; } = false;
/// <summary>
/// When true, the game's bloom (<c>VideoBloom</c>) computes its blur size as if the frame were 720 pixels high, so the
/// blur passes on the 198-pixel-high bloom buffer are the same as in a 720p frame at any render height
/// (VideoBloomPatch). Default false.
/// </summary>
public bool HighResBloomCorrection { get; set; } = false;
/// <summary>
/// When true, fixes in-game and render bugs during SetFramerate events (where objects disappear on the first frame
/// or parallax/tiles lag behind) by deferring manual camera rendering to the end of the frame after all LateUpdates.
/// Default true.
/// </summary>
public bool FixCustomFrameRate { get; set; } = true;
/// <summary>
/// When true, prevents floor tiles from being disabled when outside the camera frustum (scrFloor.OnBecameInvisible),
/// eliminating the 1-frame update lag/glitch when tiles enter the screen. Default true.
/// </summary>
public bool DisableTileCulling { get; set; } = true;
/// <summary>File extension of the default output file name, without the dot (e.g. "mkv", "mp4"); selects the container.</summary>
public string FileExtension { get; set; } = "mkv";
/// <summary>Color of the fire (red) planet during the render; null keeps the user's setting. Serialized as "#RRGGBBAA".</summary>
[JsonConverter(typeof(ColorJsonConverter))]
public Color? firePlanetColor = null;
/// <summary>Color of the ice (blue) planet during the render; null keeps the user's setting. Serialized as "#RRGGBBAA".</summary>
[JsonConverter(typeof(ColorJsonConverter))]
public Color? icePlanetColor = null;
/// <summary>
/// Color of the third (wind, planetGreen) planet during the render (PlanetarySystemPatch); null keeps the game's color.
/// Serialized as "#RRGGBBAA".
/// </summary>
[JsonConverter(typeof(ColorJsonConverter))]
public Color? windPlanetColor = null;
/// <summary>Copies every value of <paramref name="source"/>; does nothing when it is null.</summary>
public void CopyFrom(AdvancedConfig? source)
{
if (source == null) return;
RandomSeed = source.RandomSeed;
ProcessAudio = source.ProcessAudio;
NonTile = source.NonTile;
HighResBloomCorrection = source.HighResBloomCorrection;
FixCustomFrameRate = source.FixCustomFrameRate;
DisableTileCulling = source.DisableTileCulling;
FileExtension = source.FileExtension;
firePlanetColor = source.firePlanetColor;
icePlanetColor = source.icePlanetColor;
windPlanetColor = source.windPlanetColor;
}
/// <summary>Returns a copy of this object.</summary>
public AdvancedConfig Clone()
{
var clone = new AdvancedConfig();
clone.CopyFrom(this);
return clone;
}
}
/// <summary>Video encoder settings (passed to FFmpegNativeEncoder.EncoderSettings).</summary>
public class VideoEncodingConfig
{
/// <summary>Video codec (default H.264). Also selects the audio codec (CodecUtil.AudioCodecMatch).</summary>
public Codec Codec { get; set; } = Codec.H264;
/// <summary>Bitrate control mode (default CBR).</summary>
public RateControlMode RateControlMode { get; set; } = RateControlMode.CBR;
/// <summary>Target video bitrate in kbps for CBR and VBR (default 30000 = 30 Mbps).</summary>
public int Bitrate { get; set; } = 30000;
/// <summary>Peak video bitrate in kbps for VBR, used when larger than Bitrate (otherwise 1.5 × Bitrate). Default 50000.</summary>
public int MaxBitrate { get; set; } = 50000;
/// <summary>
/// Quality value for CQP: lower is higher quality. Clamped to the range of the selected encoder; a negative value
/// selects 18. Default 18.
/// </summary>
// ReSharper disable once InconsistentNaming
public int QP { get; set; } = 18;
/// <summary>Keyframe interval in seconds (GOP length = round(effective video FPS × interval) frames, at least 1). Default 2.</summary>
public double KeyframeInterval { get; set; } = 2.0;
/// <summary>When true, only software (CPU) encoders are tried; hardware encoders are skipped.</summary>
public bool ForceSoftwareEncoder { get; set; }
/// <summary>Copies every value of <paramref name="source"/>; does nothing when it is null.</summary>
public void CopyFrom(VideoEncodingConfig? source)
{
if (source == null) return;
Codec = source.Codec;
RateControlMode = source.RateControlMode;
Bitrate = source.Bitrate;
MaxBitrate = source.MaxBitrate;
QP = source.QP;
KeyframeInterval = source.KeyframeInterval;
ForceSoftwareEncoder = source.ForceSoftwareEncoder;
}
/// <summary>Returns a copy of this object.</summary>
public VideoEncodingConfig Clone()
{
var clone = new VideoEncodingConfig();
clone.CopyFrom(this);
return clone;
}
}
/// <summary>
/// Audio rendering and encoder settings. Non-positive values of the numeric settings read back as their defaults
/// (48000 Hz, 2 channels, 256000 bps).
/// </summary>
public class AudioEncodingConfig
{
private int _audioSampleRate = 48000;
private int _audioChannels = 2;
private int _audioBitrate = 256000;
/// <summary>When true, the song and sound effects are mixed offline and encoded as an audio stream. Default true.</summary>
public bool RenderAudio { get; set; } = true;
/// <summary>
/// Output sample rate in Hz (default 48000). A rate the audio codec does not support in the mod
/// (CodecUtil.AvailableSampleRate) is replaced with 48000 when the render starts.
/// </summary>
public int AudioSampleRate
{
get => _audioSampleRate <= 0 ? 48000 : _audioSampleRate;
set => _audioSampleRate = value <= 0 ? 48000 : value;
}
/// <summary>Number of output audio channels (default 2).</summary>
public int AudioChannels
{
get => _audioChannels <= 0 ? 2 : _audioChannels;
set => _audioChannels = value <= 0 ? 2 : value;
}
/// <summary>Audio bitrate in bits per second (default 256000 = 256 kbps). Unit differs from the video bitrate (kbps).</summary>
public int AudioBitrate
{
get => _audioBitrate <= 0 ? 256000 : _audioBitrate;
set => _audioBitrate = value <= 0 ? 256000 : value;
}
/// <summary>Copies every value of <paramref name="source"/>; does nothing when it is null.</summary>
public void CopyFrom(AudioEncodingConfig? source)
{
if (source == null) return;
RenderAudio = source.RenderAudio;
AudioSampleRate = source.AudioSampleRate;
AudioChannels = source.AudioChannels;
AudioBitrate = source.AudioBitrate;
}
/// <summary>Returns a copy of this object.</summary>
public AudioEncodingConfig Clone()
{
var clone = new AudioEncodingConfig();
clone.CopyFrom(this);
return clone;
}
}