using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; using UnityEngine; namespace ADOFAIRenderer.Data; /// /// Settings of one render: resolution, frame rates, encoding and advanced options. Serialized to JSON in the preset /// files (PresetManager). renders with a copy (). /// [SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")] public class RenderConfig { /// /// Game frames simulated per second of render time (the game clock advances 1 / RenderFPS seconds per frame). /// Values above render extra frames between video frames, which are not captured /// (the game state then advances in smaller time steps). /// public double RenderFPS { get; set; } /// /// Frame rate of the output video. The effective rate is min(VideoFPS, RenderFPS). /// public double VideoFPS { get; set; } /// Width of the rendered frames and the output video in pixels (also reported by the Screen.width patch). public int RenderWidth { get; set; } /// Height of the rendered frames and the output video in pixels (also reported by the Screen.height patch). public int RenderHeight { get; set; } /// Video encoder settings. public VideoEncodingConfig VideoConfig { get; set; } = new(); /// Audio rendering and encoder settings. public AudioEncodingConfig AudioConfig { get; set; } = new(); /// Advanced options (random seed, spectrum processing, tile hiding, container, planet colors). public AdvancedConfig AdvancedConfig { get; set; } = new(); /// Seconds of render time recorded after the end condition first holds (default 15). public double DelayAfterEnd { get; set; } = 15; /// Whether the judgement texts (hit accuracy texts) are shown during the render (scrHitTextManagerPatch). Default false. public bool ShowJudgementText { get; set; } = false; /// /// Copies every value of into this object; the nested configs are copied value by value /// into the existing nested objects. Does nothing when is null. /// 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); } /// Returns a deep copy (nested configs are new objects). public RenderConfig Clone() { var clone = new RenderConfig(); clone.CopyFrom(this); return clone; } } /// Advanced render options. [SuppressMessage("ReSharper", "RedundantDefaultMemberInitializer")] public class AdvancedConfig { /// /// Seed for UnityEngine.Random set before the game scene loads, so random visual effects repeat between renders; /// null leaves the random state unchanged. /// public int? RandomSeed { get; set; } = null; /// /// 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. /// public bool ProcessAudio { get; set; } = true; /// /// 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. /// public bool NonTile { get; set; } = false; /// /// When true, the game's bloom (VideoBloom) 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. /// public bool HighResBloomCorrection { get; set; } = false; /// /// 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. /// public bool FixCustomFrameRate { get; set; } = true; /// /// 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. /// public bool DisableTileCulling { get; set; } = true; /// /// When true, hides placeholder decorations (default tree-in-circle sprite) that failed to load or are empty. /// Default false. /// public bool HideFailedDecorations { get; set; } = false; /// File extension of the default output file name, without the dot (e.g. "mkv", "mp4"); selects the container. public string FileExtension { get; set; } = "mkv"; /// Color of the fire (red) planet during the render; null keeps the user's setting. Serialized as "#RRGGBBAA". [JsonConverter(typeof(ColorJsonConverter))] public Color? firePlanetColor = null; /// Color of the ice (blue) planet during the render; null keeps the user's setting. Serialized as "#RRGGBBAA". [JsonConverter(typeof(ColorJsonConverter))] public Color? icePlanetColor = null; /// /// Color of the third (wind, planetGreen) planet during the render (PlanetarySystemPatch); null keeps the game's color. /// Serialized as "#RRGGBBAA". /// [JsonConverter(typeof(ColorJsonConverter))] public Color? windPlanetColor = null; /// Copies every value of ; does nothing when it is null. 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; HideFailedDecorations = source.HideFailedDecorations; FileExtension = source.FileExtension; firePlanetColor = source.firePlanetColor; icePlanetColor = source.icePlanetColor; windPlanetColor = source.windPlanetColor; } /// Returns a copy of this object. public AdvancedConfig Clone() { var clone = new AdvancedConfig(); clone.CopyFrom(this); return clone; } } /// Video encoder settings (passed to FFmpegNativeEncoder.EncoderSettings). public class VideoEncodingConfig { /// Video codec (default H.264). Also selects the audio codec (CodecUtil.AudioCodecMatch). public Codec Codec { get; set; } = Codec.H264; /// Bitrate control mode (default CBR). public RateControlMode RateControlMode { get; set; } = RateControlMode.CBR; /// Target video bitrate in kbps for CBR and VBR (default 30000 = 30 Mbps). public int Bitrate { get; set; } = 30000; /// Peak video bitrate in kbps for VBR, used when larger than Bitrate (otherwise 1.5 × Bitrate). Default 50000. public int MaxBitrate { get; set; } = 50000; /// /// Quality value for CQP: lower is higher quality. Clamped to the range of the selected encoder; a negative value /// selects 18. Default 18. /// // ReSharper disable once InconsistentNaming public int QP { get; set; } = 18; /// Keyframe interval in seconds (GOP length = round(effective video FPS × interval) frames, at least 1). Default 2. public double KeyframeInterval { get; set; } = 2.0; /// When true, only software (CPU) encoders are tried; hardware encoders are skipped. public bool ForceSoftwareEncoder { get; set; } /// Copies every value of ; does nothing when it is null. 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; } /// Returns a copy of this object. public VideoEncodingConfig Clone() { var clone = new VideoEncodingConfig(); clone.CopyFrom(this); return clone; } } /// /// Audio rendering and encoder settings. Non-positive values of the numeric settings read back as their defaults /// (48000 Hz, 2 channels, 256000 bps). /// public class AudioEncodingConfig { private int _audioSampleRate = 48000; private int _audioChannels = 2; private int _audioBitrate = 256000; /// When true, the song and sound effects are mixed offline and encoded as an audio stream. Default true. public bool RenderAudio { get; set; } = true; /// /// 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. /// public int AudioSampleRate { get => _audioSampleRate <= 0 ? 48000 : _audioSampleRate; set => _audioSampleRate = value <= 0 ? 48000 : value; } /// Number of output audio channels (default 2). public int AudioChannels { get => _audioChannels <= 0 ? 2 : _audioChannels; set => _audioChannels = value <= 0 ? 2 : value; } /// Audio bitrate in bits per second (default 256000 = 256 kbps). Unit differs from the video bitrate (kbps). public int AudioBitrate { get => _audioBitrate <= 0 ? 256000 : _audioBitrate; set => _audioBitrate = value <= 0 ? 256000 : value; } /// Copies every value of ; does nothing when it is null. public void CopyFrom(AudioEncodingConfig? source) { if (source == null) return; RenderAudio = source.RenderAudio; AudioSampleRate = source.AudioSampleRate; AudioChannels = source.AudioChannels; AudioBitrate = source.AudioBitrate; } /// Returns a copy of this object. public AudioEncodingConfig Clone() { var clone = new AudioEncodingConfig(); clone.CopyFrom(this); return clone; } }