diff --git a/API/RenderSession.cs b/API/RenderSession.cs
index d5ebd4a..c2e2271 100644
--- a/API/RenderSession.cs
+++ b/API/RenderSession.cs
@@ -76,6 +76,11 @@ public class RenderSession
return this;
}
+ ///
+ /// Sets the custom output path for the rendered file.
+ ///
+ /// The file path where the output will be saved, or null to use the default capture directory.
+ /// The current instance for method chaining.
public RenderSession WithOutputPath(string? outputPath)
{
_outputPath = outputPath;
diff --git a/Core/RenderManager.cs b/Core/RenderManager.cs
index 4c6c031..c150430 100644
--- a/Core/RenderManager.cs
+++ b/Core/RenderManager.cs
@@ -502,10 +502,12 @@ namespace ADOFAIRenderer.Core
var videoFPS = Configuration.VideoFPS;
var startTime = DateTime.Now;
- var fileName = startTime.ToString("yyyy-MM-dd HH-mm-ss") + $".{Configuration.AdvancedConfig.Extension}";
- _currentOutPath = Path.Combine(MelonEnvironment.GameRootDirectory, "Render", fileName);
-
if (RenderContext.OutputPath != null) _currentOutPath = RenderContext.OutputPath;
+ else {
+ var fileName = startTime.ToString("yyyy-MM-dd HH-mm-ss") +
+ $".{Configuration.AdvancedConfig.FileExtension.TrimStart('.').ToLower()}";
+ _currentOutPath = Path.Combine(MelonEnvironment.GameRootDirectory, "Render", fileName);
+ }
FFmpegNativeEncoder? videoFFmpeg = null;
try
@@ -536,8 +538,6 @@ namespace ADOFAIRenderer.Core
videoFFmpeg = new FFmpegNativeEncoder(settings);
- SetupCaptureDirectory();
-
_activeVideoFFmpeg = videoFFmpeg;
lock (_allVideoFFmpegInstances)
{
diff --git a/Data/RenderConfig.cs b/Data/RenderConfig.cs
index bae11c9..33235c1 100644
--- a/Data/RenderConfig.cs
+++ b/Data/RenderConfig.cs
@@ -26,7 +26,7 @@ namespace ADOFAIRenderer.Data
public int? RandomSeed { get; set; } = null;
public bool ProcessAudio { get; set; } = true;
public bool NonTile { get; set; } = false;
- public string Extension { get; set; } = "mkv";
+ public string FileExtension { get; set; } = "mkv";
}
public class VideoEncodingConfig
diff --git a/UI/RenderConfigUIManager.cs b/UI/RenderConfigUIManager.cs
index bd66328..0190fed 100644
--- a/UI/RenderConfigUIManager.cs
+++ b/UI/RenderConfigUIManager.cs
@@ -124,7 +124,7 @@ namespace ADOFAIRenderer.UI
ProcessAudio = config.AdvancedConfig.ProcessAudio,
RandomSeed = config.AdvancedConfig.RandomSeed,
NonTile = config.AdvancedConfig.NonTile,
- Extension = config.AdvancedConfig.Extension
+ FileExtension = config.AdvancedConfig.FileExtension
};
if (_advancedConfigContainer != null && _root.Contains(_advancedConfigContainer))
@@ -162,18 +162,18 @@ namespace ADOFAIRenderer.UI
_advancedConfigContainer.Add(titleRow);
// 2. 확장자 로우
- var extensionRow = CreateRow();
- var extensionLabel = CreateFormLabel("확장자");
- var extensionChoices = new List { "mkv", "mp4", "mov", "webm", "avi" };
- var currentExt = string.IsNullOrWhiteSpace(tempConfig.Extension) ? "mkv" : tempConfig.Extension.TrimStart('.').ToLower();
- if (!extensionChoices.Contains(currentExt))
+ var fileExtensionRow = CreateRow();
+ var fileExtensionLabel = CreateFormLabel("확장자");
+ var fileExtensionChoices = new List { "mkv", "mp4", "mov", "webm", "avi" };
+ var currentFileExt = string.IsNullOrWhiteSpace(tempConfig.FileExtension) ? "mkv" : tempConfig.FileExtension.TrimStart('.').ToLower();
+ if (!fileExtensionChoices.Contains(currentFileExt))
{
- extensionChoices.Add(currentExt);
+ fileExtensionChoices.Add(currentFileExt);
}
- var extensionDropdown = new DropdownField(extensionChoices, currentExt) { name = "AdvancedExtension" };
- extensionRow.Add(extensionLabel);
- extensionRow.Add(extensionDropdown);
- _advancedConfigContainer.Add(extensionRow);
+ var fileExtensionDropdown = new DropdownField(fileExtensionChoices, currentFileExt) { name = "AdvancedFileExtension" };
+ fileExtensionRow.Add(fileExtensionLabel);
+ fileExtensionRow.Add(fileExtensionDropdown);
+ _advancedConfigContainer.Add(fileExtensionRow);
// 3. 오디오 처리 로우
var processAudioRow = CreateRow();
@@ -227,7 +227,7 @@ namespace ADOFAIRenderer.UI
saveBtn.RegisterCallback(_ =>
{
- tempConfig.Extension = extensionDropdown.value;
+ tempConfig.FileExtension = fileExtensionDropdown.value;
tempConfig.ProcessAudio = processAudioToggle.value;
tempConfig.NonTile = nonTileToggle.value;
if (int.TryParse(randomSeedField.value?.Trim(), out var seed))
@@ -408,7 +408,7 @@ namespace ADOFAIRenderer.UI
modeDropdown.RegisterValueChangedCallback(evt => UpdateVisibility(evt.newValue));
qpField.RegisterValueChangedCallback(evt =>
{
- warningRow.style.display = modeDropdown.value == RateControlMode.CQP.ToString() && evt.newValue == 0
+ warningRow.style.display = modeDropdown.value == nameof(RateControlMode.CQP) && evt.newValue == 0
? DisplayStyle.Flex
: DisplayStyle.None;
});
@@ -739,15 +739,21 @@ namespace ADOFAIRenderer.UI
_root.Remove(_presetConfigContainer);
}
- _presetConfigContainer = new VisualElement { name = "PresetSettingsContainer" };
- _presetConfigContainer.style.position = Position.Absolute;
- _presetConfigContainer.style.width = Length.Percent(100);
- _presetConfigContainer.style.height = Length.Percent(100);
- _presetConfigContainer.style.left = 0;
- _presetConfigContainer.style.top = 0;
- _presetConfigContainer.style.backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1.0f));
- _presetConfigContainer.style.justifyContent = Justify.Center;
- _presetConfigContainer.style.alignItems = Align.Center;
+ _presetConfigContainer = new VisualElement
+ {
+ name = "PresetSettingsContainer",
+ style =
+ {
+ position = Position.Absolute,
+ width = Length.Percent(100),
+ height = Length.Percent(100),
+ left = 0,
+ top = 0,
+ backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1.0f)),
+ justifyContent = Justify.Center,
+ alignItems = Align.Center
+ }
+ };
// Title Row
var titleRow = CreateRow();
@@ -837,152 +843,7 @@ namespace ADOFAIRenderer.UI
highButton.style.display = DisplayStyle.None;
}
- // Refresh Preset List
- System.Action refreshPresets = null!;
- refreshPresets = () =>
- {
- scrollView.Clear();
- var presets = PresetManager.GetPresets();
- if (presets.Count == 0)
- {
- var emptyRow = CreateRow();
- emptyRow.style.height = StyleKeyword.Auto;
- emptyRow.style.minHeight = StyleKeyword.Null;
- emptyRow.style.maxHeight = StyleKeyword.Null;
-
- var emptyLabel = new Label("저장된 프리셋이 없습니다.")
- {
- style =
- {
- color = new StyleColor(Color.gray),
- unityTextAlign = TextAnchor.MiddleCenter,
- marginTop = 80,
- fontSize = 20
- }
- };
- emptyRow.Add(emptyLabel);
- scrollView.Add(emptyRow);
- }
- else
- {
- for (int i = 0; i < presets.Count; i++)
- {
- var preset = presets[i];
- var itemRow = new VisualElement
- {
- style =
- {
- flexDirection = FlexDirection.Row,
- justifyContent = Justify.SpaceBetween,
- alignItems = Align.Center,
- paddingBottom = 5,
- paddingTop = 5,
- borderBottomWidth = (i == presets.Count - 1) ? 0 : 1,
- borderBottomColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f, 0.8f))
- }
- };
- itemRow.AddToClassList("ui-row");
-
- var nameLabel = new Label(preset.Name)
- {
- style =
- {
- color = new StyleColor(Color.white),
- fontSize = 30,
- flexGrow = 1
- }
- };
-
- var btnContainer = new VisualElement
- {
- style = { flexDirection = FlexDirection.Row }
- };
-
- var loadBtn = new Button { text = "불러오기" };
- loadBtn.style.marginRight = 10;
- loadBtn.style.width = 180f;
- loadBtn.style.marginTop = 0f;
- loadBtn.style.marginBottom = 0f;
- loadBtn.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.22f, 1.0f));
- loadBtn.style.borderTopColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
- loadBtn.style.borderBottomColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
- loadBtn.style.borderLeftColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
- loadBtn.style.borderRightColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
- loadBtn.style.borderTopWidth = 1f;
- loadBtn.style.borderBottomWidth = 1f;
- loadBtn.style.borderLeftWidth = 1f;
- loadBtn.style.borderRightWidth = 1f;
- loadBtn.style.borderTopLeftRadius = 8f;
- loadBtn.style.borderTopRightRadius = 8f;
- loadBtn.style.borderBottomLeftRadius = 8f;
- loadBtn.style.borderBottomRightRadius = 8f;
- loadBtn.style.paddingLeft = 10f;
- loadBtn.style.paddingRight = 10f;
- loadBtn.style.paddingTop = 10f;
- loadBtn.style.paddingBottom = 10f;
- loadBtn.style.fontSize = 30f;
- loadBtn.style.height = 80f;
- loadBtn.style.color = new StyleColor(Color.white);
-
- loadBtn.RegisterCallback(_ =>
- {
- ApplyConfigToUI(preset.Config);
- ToggleScreens(false);
- _root.Remove(_presetConfigContainer);
- _presetConfigContainer = null;
- });
-
- var deleteBtn = new Button { text = "삭제" };
- deleteBtn.style.width = 120f;
- deleteBtn.style.marginTop = 0f;
- deleteBtn.style.marginBottom = 0f;
- deleteBtn.style.backgroundColor = new StyleColor(new Color(0.25f, 0.12f, 0.12f, 1.0f));
- deleteBtn.style.borderTopColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
- deleteBtn.style.borderBottomColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
- deleteBtn.style.borderLeftColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
- deleteBtn.style.borderRightColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
- deleteBtn.style.borderTopWidth = 1f;
- deleteBtn.style.borderBottomWidth = 1f;
- deleteBtn.style.borderLeftWidth = 1f;
- deleteBtn.style.borderRightWidth = 1f;
- deleteBtn.style.borderTopLeftRadius = 8f;
- deleteBtn.style.borderTopRightRadius = 8f;
- deleteBtn.style.borderBottomLeftRadius = 8f;
- deleteBtn.style.borderBottomRightRadius = 8f;
- deleteBtn.style.paddingLeft = 10f;
- deleteBtn.style.paddingRight = 10f;
- deleteBtn.style.paddingTop = 10f;
- deleteBtn.style.paddingBottom = 10f;
- deleteBtn.style.fontSize = 30f;
- deleteBtn.style.height = 80f;
- deleteBtn.style.color = new StyleColor(Color.white);
-
- deleteBtn.RegisterCallback(_ =>
- {
- CommonWarningDialogUI.ShowDialog(
- "프리셋 삭제",
- $"'{preset.Name}' 프리셋을 정말 삭제하시겠습니까?",
- new DialogButton("예", () =>
- {
- PresetManager.DeletePreset(preset.Name);
- refreshPresets();
- }),
- new DialogButton("아니오", () => {})
- );
- });
-
- btnContainer.Add(loadBtn);
- btnContainer.Add(deleteBtn);
-
- itemRow.Add(nameLabel);
- itemRow.Add(btnContainer);
-
- scrollView.Add(itemRow);
- }
- }
- };
-
- refreshPresets();
+ RefreshPresets();
// Save row: Label + TextField + Save Button
var saveRow = CreateRow();
@@ -991,10 +852,16 @@ namespace ADOFAIRenderer.UI
var nameLabelLabel = CreateFormLabel("새 프리셋 이름");
nameLabelLabel.style.marginRight = 10;
- var nameField = new TextField { label = "", name = "NewPresetName" };
- nameField.style.width = 400;
- nameField.style.marginRight = 20;
-
+ var nameField = new TextField
+ {
+ label = "", name = "NewPresetName",
+ style =
+ {
+ width = 400,
+ marginRight = 20
+ }
+ };
+
// 기존 폼 입력 필드 스타일과 맞춤
var textInput = nameField.Q("unity-text-input");
if (textInput != null)
@@ -1073,7 +940,7 @@ namespace ADOFAIRenderer.UI
PresetManager.SavePreset(presetName, currentConfig);
nameField.value = string.Empty;
- refreshPresets();
+ RefreshPresets();
}
catch (System.Exception ex)
{
@@ -1108,6 +975,137 @@ namespace ADOFAIRenderer.UI
ToggleScreens(true);
return;
+ // Refresh Preset List
+ void RefreshPresets()
+ {
+ scrollView.Clear();
+ var presets = PresetManager.GetPresets();
+ if (presets.Count == 0)
+ {
+ var emptyRow = CreateRow();
+ emptyRow.style.height = StyleKeyword.Auto;
+ emptyRow.style.minHeight = StyleKeyword.Null;
+ emptyRow.style.maxHeight = StyleKeyword.Null;
+
+ var emptyLabel = new Label("저장된 프리셋이 없습니다.") { style = { color = new StyleColor(Color.gray), unityTextAlign = TextAnchor.MiddleCenter, marginTop = 80, fontSize = 20 } };
+ emptyRow.Add(emptyLabel);
+ scrollView.Add(emptyRow);
+ }
+ else
+ {
+ for (int i = 0; i < presets.Count; i++)
+ {
+ var preset = presets[i];
+ var itemRow = new VisualElement
+ {
+ style =
+ {
+ flexDirection = FlexDirection.Row,
+ justifyContent = Justify.SpaceBetween,
+ alignItems = Align.Center,
+ paddingBottom = 5,
+ paddingTop = 5,
+ borderBottomWidth = (i == presets.Count - 1) ? 0 : 1,
+ borderBottomColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f, 0.8f))
+ }
+ };
+ itemRow.AddToClassList("ui-row");
+
+ var nameLabel = new Label(preset.Name) { style = { color = new StyleColor(Color.white), fontSize = 30, flexGrow = 1 } };
+
+ var btnContainer = new VisualElement { style = { flexDirection = FlexDirection.Row } };
+
+ var loadBtn = new Button
+ {
+ text = "불러오기",
+ style =
+ {
+ marginRight = 10,
+ width = 180f,
+ marginTop = 0f,
+ marginBottom = 0f,
+ backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.22f, 1.0f)),
+ borderTopColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f)),
+ borderBottomColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f)),
+ borderLeftColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f)),
+ borderRightColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f)),
+ borderTopWidth = 1f,
+ borderBottomWidth = 1f,
+ borderLeftWidth = 1f,
+ borderRightWidth = 1f,
+ borderTopLeftRadius = 8f,
+ borderTopRightRadius = 8f,
+ borderBottomLeftRadius = 8f,
+ borderBottomRightRadius = 8f,
+ paddingLeft = 10f,
+ paddingRight = 10f,
+ paddingTop = 10f,
+ paddingBottom = 10f,
+ fontSize = 30f,
+ height = 80f,
+ color = new StyleColor(Color.white)
+ }
+ };
+
+ loadBtn.RegisterCallback(_ =>
+ {
+ ApplyConfigToUI(preset.Config);
+ ToggleScreens(false);
+ _root.Remove(_presetConfigContainer);
+ _presetConfigContainer = null;
+ });
+
+ var deleteBtn = new Button
+ {
+ text = "삭제",
+ style =
+ {
+ width = 120f,
+ marginTop = 0f,
+ marginBottom = 0f,
+ backgroundColor = new StyleColor(new Color(0.25f, 0.12f, 0.12f, 1.0f)),
+ borderTopColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f)),
+ borderBottomColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f)),
+ borderLeftColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f)),
+ borderRightColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f)),
+ borderTopWidth = 1f,
+ borderBottomWidth = 1f,
+ borderLeftWidth = 1f,
+ borderRightWidth = 1f,
+ borderTopLeftRadius = 8f,
+ borderTopRightRadius = 8f,
+ borderBottomLeftRadius = 8f,
+ borderBottomRightRadius = 8f,
+ paddingLeft = 10f,
+ paddingRight = 10f,
+ paddingTop = 10f,
+ paddingBottom = 10f,
+ fontSize = 30f,
+ height = 80f,
+ color = new StyleColor(Color.white)
+ }
+ };
+
+ deleteBtn.RegisterCallback(_ =>
+ {
+ CommonWarningDialogUI.ShowDialog("프리셋 삭제", $"'{preset.Name}' 프리셋을 정말 삭제하시겠습니까?", new DialogButton("예", () =>
+ {
+ PresetManager.DeletePreset(preset.Name);
+ RefreshPresets();
+ }), new DialogButton("아니오", () => { }));
+ });
+
+ btnContainer.Add(loadBtn);
+ btnContainer.Add(deleteBtn);
+
+ itemRow.Add(nameLabel);
+ itemRow.Add(btnContainer);
+
+ scrollView.Add(itemRow);
+ }
+ }
+ }
+
void ToggleScreens(bool showPreset)
{
if (_root.childCount > 0)