using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditorInternal; using UnityEngine; using UnityObject = UnityEngine.Object; namespace UnityEditor.Recorder { /// /// Use this class to manage the recording settings (frame rate, frame range, list of Recorder Settings) for the . /// public class RecorderControllerSettings : ScriptableObject { [SerializeField] RecordMode m_RecordMode = RecordMode.Manual; [SerializeField] FrameRatePlayback m_FrameRatePlayback = FrameRatePlayback.Constant; [SerializeField] FrameRateType m_FrameRateType = FrameRateType.FR_30; [SerializeField] float m_CustomFrameRateValue = 30.0f; [SerializeField] int m_StartFrame; [SerializeField] int m_EndFrame; [SerializeField] float m_StartTime; [SerializeField] float m_EndTime; [SerializeField] bool m_CapFrameRate = true; [SerializeField] bool m_ExitPlayMode = true; static readonly Dictionary s_FPSToValue = new Dictionary() { { FrameRateType.FR_23, 24 * 1000 / 1001f }, { FrameRateType.FR_24, 24 }, { FrameRateType.FR_25, 25 }, { FrameRateType.FR_29, 30 * 1000 / 1001f }, { FrameRateType.FR_30, 30 }, { FrameRateType.FR_50, 50 }, { FrameRateType.FR_59, 60 * 1000 / 1001f }, { FrameRateType.FR_60, 60 } }; void OnValidate() { m_CustomFrameRateValue = Mathf.Max(2 * float.Epsilon, m_CustomFrameRateValue); // Eps complains about the invalid frame rate. } internal RecordMode RecordMode => m_RecordMode; /// /// Instructs the recorder to exit Play Mode once the recording has finished. /// public bool ExitPlayMode { get { return m_ExitPlayMode; } set { m_ExitPlayMode = value; } } /// /// Indicates the type of frame rate (constant or variable) for the current list of Recorders. /// public FrameRatePlayback FrameRatePlayback { get { return m_FrameRatePlayback; } set { m_FrameRatePlayback = value; } } /// /// Allows setting and retrieving the frame rate for the current list of Recorders. /// public float FrameRate { get { return m_FrameRateType == FrameRateType.FR_CUSTOM ? m_CustomFrameRateValue : s_FPSToValue[m_FrameRateType]; } set { m_FrameRateType = FrameRateType.FR_CUSTOM; m_CustomFrameRateValue = value; } } /// /// Sets the Recorders to Manual mode. /// public void SetRecordModeToManual() { m_RecordMode = RecordMode.Manual; } /// /// Sets the Recorders to Single Frame recording mode. /// /// The frame to be recorded. public void SetRecordModeToSingleFrame(int frameNumber) { m_RecordMode = RecordMode.SingleFrame; m_StartFrame = m_EndFrame = frameNumber; } /// /// Sets the Recorders to Frame Interval mode and defines the Start and End frame of the interval to record. /// /// Start frame. /// End frame. public void SetRecordModeToFrameInterval(int startFrame, int endFrame) { m_RecordMode = RecordMode.FrameInterval; m_StartFrame = startFrame; m_EndFrame = endFrame; } /// /// Sets the Recorders to Time Interval mode and defines the Start and End times of the interval to record. /// /// The start time, in seconds. /// The end time, in seconds. public void SetRecordModeToTimeInterval(float startTime, float endTime) { m_RecordMode = RecordMode.TimeInterval; m_StartTime = startTime; m_EndTime = endTime; } /// /// Indicates if the Recorders frame rate should cap the Unity rendering frame rate. When enabled, Unity is prevented from rendering faster than the set FrameRate. /// public bool CapFrameRate { get { return m_CapFrameRate; } set { m_CapFrameRate = value; } } [SerializeField] List m_RecorderSettings = new List(); string m_Path; /// /// Loads or creates Recorder Settings to the specified file path. /// /// The path to load or create Recorder Settings. /// The loaded or created Recorder Settings. public static RecorderControllerSettings LoadOrCreate(string path) { RecorderControllerSettings prefs; try { var objs = InternalEditorUtility.LoadSerializedFileAndForget(path); prefs = objs.FirstOrDefault(p => p is RecorderControllerSettings) as RecorderControllerSettings; } catch (Exception e) { Debug.LogError("Unhandled exception while loading Recorder preferences: " + e); prefs = null; } if (prefs == null) { prefs = CreateInstance(); prefs.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy; prefs.name = "Global Settings"; prefs.Save(); } prefs.m_Path = path; return prefs; } /// /// Get the global controller settings. /// /// The global RecorderControllerSettings public static RecorderControllerSettings GetGlobalSettings() { var globalPath = Path.Combine(Application.dataPath, "..", "Library", "Recorder", "recorder.pref"); return LoadOrCreate(globalPath); } internal void ReleaseRecorderSettings() { foreach (var recorder in m_RecorderSettings) { DestroyImmediate(recorder); } ClearRecorderSettings(); } internal void ClearRecorderSettings() { m_RecorderSettings.Clear(); } /// /// Stores the collection of Recorder Settings instances. /// public IEnumerable RecorderSettings { get { foreach (var setting in m_RecorderSettings) { if (setting != null) // It's important to keep null entries because they could be recorderSettings that rely on external code (that did not compile for now) yield return setting; } } } /// /// Adds a new instance of Recorder Settings to the current collection. /// /// The Recorder Settings instance to add. public void AddRecorderSettings(RecorderSettings recorder) { EditorUtility.SetDirty(this); Undo.RegisterCompleteObjectUndo(this, "Add Recorder"); if (!m_RecorderSettings.Contains(recorder)) { AddRecorderInternal(recorder); Save(); } } /// /// Removes an instance of Recorder Settings from the current collection. /// /// The Recorder settings instance to be removed. public void RemoveRecorder(RecorderSettings recorder) { if (m_RecorderSettings.Contains(recorder)) { EditorUtility.SetDirty(this); Undo.RegisterCompleteObjectUndo(this, "Remove Recorder"); m_RecorderSettings.Remove(recorder); Undo.DestroyObjectImmediate(recorder); Save(); } } /// /// Saves the current list of Recorder Settings instances to disk. /// public void Save() { if (string.IsNullOrEmpty(m_Path)) return; try { var directory = Path.GetDirectoryName(m_Path); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) Directory.CreateDirectory(directory); var recordersCopy = RecorderSettings.ToArray(); var objs = new UnityObject[recordersCopy.Length + 1]; objs[0] = this; for (int i = 0; i < recordersCopy.Length; ++i) objs[i + 1] = recordersCopy[i]; InternalEditorUtility.SaveToSerializedFileAndForget(objs, m_Path, true); } catch (Exception e) { Debug.LogError("Unhandled exception while saving Recorder settings: " + e); } } internal void ApplyGlobalSetting(RecorderSettings recorder) { recorder.RecordMode = m_RecordMode; recorder.FrameRatePlayback = m_FrameRatePlayback; recorder.FrameRate = FrameRate; recorder.StartFrame = m_StartFrame; recorder.EndFrame = m_EndFrame; recorder.StartTime = m_StartTime; recorder.EndTime = m_EndTime; recorder.CapFrameRate = m_CapFrameRate; recorder.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy; } internal void ApplyGlobalSettingToAllRecorders() { foreach (var recorder in RecorderSettings) ApplyGlobalSetting(recorder); } void AddRecorderInternal(RecorderSettings recorder) { ApplyGlobalSetting(recorder); m_RecorderSettings.Add(recorder); } /// /// Gets the number of currently active recorders that capture accumulation. /// /// The number of recorders that are currently capturing accumulation. int GetNumberOfActiveSubFrameRecorders() { return RecorderSettings.Count(r => r.Enabled && UnityHelpers.CaptureAccumulation(r)); } /// /// Whether the current context should prevent recording due to invalid accumulation settings or not. /// /// internal bool InvalidContextBecauseOfAccumulation() { return GetNumberOfActiveSubFrameRecorders() >= 1 && RecorderSettings.Count(r => r.Enabled) > 1; } } }