Finalize First O5Kit Config UI Prototype

This commit is contained in:
copecone committed 2026-10-05 23:37:11 +09:00
1 parent 1d4b1cdd46
commit b3e50cd50e
4 files changed
+172 -154

No files matched your search

+2 -2
View File
@@ -366,7 +366,7 @@ internal class RenderManager : MonoBehaviour
{ {
if (Configuration.AudioConfig.RenderAudio && Configuration.VideoConfig.Codec is Codec.VP9 or Codec.AV1 or Codec.VVC) if (Configuration.AudioConfig.RenderAudio && Configuration.VideoConfig.Codec is Codec.VP9 or Codec.AV1 or Codec.VVC)
{ {
Configuration.AudioConfig.AudioSampleRate = 48000; if (Configuration.AudioConfig.AudioSampleRate == 44100) Configuration.AudioConfig.AudioSampleRate = 48000;
} }
for (; countdown > 0; countdown--) for (; countdown > 0; countdown--)
@@ -775,7 +775,7 @@ internal class RenderManager : MonoBehaviour
_frameReader?.Dispose(); _frameReader?.Dispose();
AudioMixer?.Reset(); AudioMixer = null; AudioMixer?.Reset(); AudioMixer = null;
SyncCalculator.Reset(); SyncCalculator.Reset();
RenderContext.Reset(); RenderContext.Reset();
ADOBase.loader.LoadSceneWithTransition(WipeDirection.StartsFromRight, "scnEditor"); ADOBase.loader.LoadSceneWithTransition(WipeDirection.StartsFromRight, "scnEditor");
+1 -1
View File
@@ -44,7 +44,7 @@ public class VideoEncodingConfig
public class AudioEncodingConfig public class AudioEncodingConfig
{ {
private int _audioSampleRate = 44100; private int _audioSampleRate = 48000;
private int _audioChannels = 2; private int _audioChannels = 2;
private int _audioBitrate = 256000; private int _audioBitrate = 256000;
+23 -1
View File
@@ -80,7 +80,29 @@ internal class RendererUIManager : MonoBehaviour
MiddleClickToDefault = true, MiddleClickToDefault = true,
SliderSensitivity = 1.0f SliderSensitivity = 1.0f
}, },
theme: O5Theme.Dark, theme: O5Theme.Dark with
{
PanelBG = new Color(0.067f, 0.067f, 0.067f, 1f),
TopBar = new Color(0f, 0f, 0f, 0.6f),
MenuBG = new Color(0f, 0f, 0f, 0.6f),
MenuHover = new Color(1.000f, 0.294f, 0.079f, .6f),
ObjectBG = new Color(0f, 0f, 0f, 1f),
ObjectButton = new Color(0f, 0f, 0f, 1f),
ObjectActive = new Color(1.000f, 0.294f, 0.079f, 1f),
ObjectActiveBright = new Color(0.812f, 0.827f, 1.000f, 1f),
ControlOutlineIdle = new Color(1f, 1f, 1f, 0.4f),
ButtonHover = new Color(0.133f, 0.133f, 0.133f, 1.0f),
ControlOutlineHover = new Color(1f, 1f, 1f, 0.4f),
CardHeader = new Color(0.157f, 0f, 0f, 1f),
CardPanel = new Color(0, 0, 0, 0f),
SoftRed = new Color(1.000f, 0.576f, 0.590f, 1f),
MathOk = new Color(0.408f, 0.827f, 0.569f, 1f),
MathWarn = new Color(0.964f, 0.647f, 0.315f, 1f),
MathErr = new Color(1.000f, 0.576f, 0.590f, 1f),
CornerRadius = 12, OutlineWidth = 2, ControlHeight = 40, FontSizeBody = 18, FontSizeH1 = 48,
Outline = new Color(1f, 1f, 1f, 0.2f),
TextDimAlpha = 1f
},
fonts: new RendererFontProvider() fonts: new RendererFontProvider()
); );
+146 -150
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using ADOFAIRenderer.API; using ADOFAIRenderer.API;
using ADOFAIRenderer.Core; using ADOFAIRenderer.Core;
using ADOFAIRenderer.Data; using ADOFAIRenderer.Data;
@@ -21,11 +22,11 @@ internal static class RenderConfigWindow
private static O5Window? _configWindow; private static O5Window? _configWindow;
// Controls cache for validation & sync // Controls cache for validation & sync
private static O5InputField? _renderFpsInput; private static O5Slider? _renderFpsSlider;
private static O5InputField? _videoFpsInput; private static O5Slider? _videoFPSSlider;
private static O5InputField? _renderWidthInput; private static O5Slider? _renderWidthSlider;
private static O5InputField? _renderHeightInput; private static O5Slider? _renderHeightSlider;
private static O5InputField? _delayAfterEndInput; private static O5Slider? _delayAfterEndSlider;
private static O5Toggle? _showJudgementToggle; private static O5Toggle? _showJudgementToggle;
private static O5Dropdown<Codec>? _codecDropdown; private static O5Dropdown<Codec>? _codecDropdown;
@@ -33,13 +34,13 @@ internal static class RenderConfigWindow
private static O5InputField? _bitrateInput; private static O5InputField? _bitrateInput;
private static O5InputField? _maxBitrateInput; private static O5InputField? _maxBitrateInput;
private static O5InputField? _qpInput; private static O5InputField? _qpInput;
private static O5InputField? _keyframeIntervalInput; private static O5Slider? _keyframeIntervalSlider;
private static O5Toggle? _forceSoftwareToggle; private static O5Toggle? _forceSoftwareToggle;
private static O5Toggle? _renderAudioToggle; private static O5Toggle? _renderAudioToggle;
private static O5Dropdown<int>? _sampleRateDropdown; private static O5Dropdown<int>? _sampleRateDropdown;
private static O5Dropdown<int>? _channelsDropdown; private static O5Dropdown<int>? _channelsDropdown;
private static O5InputField? _audioBitrateInput; private static O5Slider? _audioBitrateSlider;
private static O5InputField? _outputDirInput; private static O5InputField? _outputDirInput;
@@ -69,7 +70,8 @@ internal static class RenderConfigWindow
BuildUI(_configWindow.Content); BuildUI(_configWindow.Content);
} }
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
var config = RenderManager.Instance.Configuration ??= new RenderConfig(); var config = RenderManager.Instance.Configuration ??= new RenderConfig();
ApplyConfigToUI(config); ApplyConfigToUI(config);
@@ -83,58 +85,88 @@ internal static class RenderConfigWindow
var (viewport, scrollContent, scroller) = var (viewport, scrollContent, scroller) =
O5Factory.ScrollView(ctx, content, spacing: 10f, padding: 8f); O5Factory.ScrollView(ctx, content, spacing: 10f, padding: 8f);
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
var config = RenderManager.Instance.Configuration ??= new RenderConfig(); var config = RenderManager.Instance.Configuration ??= new RenderConfig();
EnsureDefaultConfig(config); EnsureDefaultConfig(config);
double[] renderFpsSnap = { 23.976, 24, 29.97, 30, 59.94, 60, 144,
120, 240, 360, 480, 600, 720, // +120
288, 576, 864, 1152, 1440, 1728, // +288
3000 // MAX
};
double[] videoFpsSnap = { 23.976, 24, 29.97, 30, 59.94, 60, 72,
120, 180, 240, 300, 360, 420, 480, 540, // +60
144, 216, 288, 432, 504, 576, // +72
600 // MAX
};
double[] audioBitrateSnap = { 64, 96, 128, 192, 256, 320, 384 };
// 1-3. Top section: 좌/우 컨테이너 1개씩으로 3행을 쌓는다 (중앙선 1개) // 1-3. Top section: 좌/우 컨테이너 1개씩으로 3행을 쌓는다 (중앙선 1개)
var (topLeft, topRight) = CreateTwoColumnPanel(scrollContent, spacing: 8f, columnSpacing: 10f); var (topLeft, topRight) = CreateTwoColumnPanel(scrollContent, spacing: 8f, columnSpacing: 10f);
_renderFpsInput = CreateInputField(
ctx, CreateFieldSlot(topLeft), _renderFpsSlider = O5Factory.Slider(
LocalizationManager.Get("ui.render_config.render_fps"), ctx: ctx, parent: CreateFieldSlot(topLeft),
"60", config.RenderFPS.ToString("0.##"), defaultValue: 60, min: 0.01, max: 3000, value: 60,
"input_render_fps", format: "F3", clampMode: ClampMode.All,
TMPro.TMP_InputField.CharacterValidation.Decimal,
val => { if (double.TryParse(val, out var d) && d > 0) config.RenderFPS = d; } sliderFilter: value => renderFpsSnap.OrderBy(n => Math.Abs(n - value)).First(),
inputFilter: null, onComplete: null, onChanged: val => { config.RenderFPS = val; },
text: LocalizationManager.Get("ui.render_config.render_fps"),
id: "slider_render_fps", showFill: true
); );
_renderWidthInput = CreateInputField(
ctx, CreateFieldSlot(topLeft), _videoFPSSlider = O5Factory.Slider(
LocalizationManager.Get("ui.render_config.render_width"), ctx: ctx, parent: CreateFieldSlot(topRight),
"1920", config.RenderWidth.ToString(), defaultValue: 60, min: 0.01, max: 600, value: 60,
"input_render_width", format: "F3", clampMode: ClampMode.All,
TMPro.TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var i) && i > 0) config.RenderWidth = i; } sliderFilter: value => videoFpsSnap.OrderBy(n => Math.Abs(n - value)).First(),
inputFilter: null, onComplete: null, onChanged: val => { config.VideoFPS = val; },
text: LocalizationManager.Get("ui.render_config.video_fps"),
id: "slider_video_fps", showFill: true
); );
_delayAfterEndInput = CreateInputField(
ctx, CreateFieldSlot(topLeft), _renderWidthSlider = O5Factory.Slider(
LocalizationManager.Get("ui.render_config.delay_after_end"), ctx: ctx, parent: CreateFieldSlot(topLeft),
"1.0", config.DelayAfterEnd.ToString("0.##"), defaultValue: 2560, min: 0, max: 15360, value: 2560,
"input_delay_after_end", format: "F0", clampMode: ClampMode.All,
TMPro.TMP_InputField.CharacterValidation.Decimal,
val => { if (double.TryParse(val, out var d) && d >= 0) config.DelayAfterEnd = d; } sliderFilter: value => Math.Max(value, 2), inputFilter: value => Math.Max(value, 2),
onComplete: null, onChanged: val => { config.RenderWidth = (int)Math.Round(val); },
text: LocalizationManager.Get("ui.render_config.render_width"),
id: "slider_render_width", showFill: true
); );
_videoFpsInput = CreateInputField(
ctx, CreateFieldSlot(topRight), _renderHeightSlider = O5Factory.Slider(
LocalizationManager.Get("ui.render_config.video_fps"), ctx: ctx, parent: CreateFieldSlot(topRight),
"60", config.VideoFPS.ToString("0.##"), defaultValue: 1440, min: 0, max: 8640, value: 1440,
"input_video_fps", format: "F0", clampMode: ClampMode.All,
TMPro.TMP_InputField.CharacterValidation.Decimal,
val => { if (double.TryParse(val, out var d) && d > 0) config.VideoFPS = d; } sliderFilter: value => Math.Max(value, 2), inputFilter: value => Math.Max(value, 2),
onComplete: null, onChanged: val => { config.RenderWidth = (int)Math.Round(val); },
text: LocalizationManager.Get("ui.render_config.render_height"),
id: "slider_render_height", showFill: true
); );
_renderHeightInput = CreateInputField(
ctx, CreateFieldSlot(topRight), _delayAfterEndSlider = O5Factory.Slider(
LocalizationManager.Get("ui.render_config.render_height"), ctx: ctx, parent: CreateFieldSlot(topLeft),
"1080", config.RenderHeight.ToString(), defaultValue: 15, min: 0, max: 9999.999, value: 15,
"input_render_height", format: "F3", clampMode: ClampMode.All,
TMPro.TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var i) && i > 0) config.RenderHeight = i; } sliderFilter: null, inputFilter: null,
onComplete: null, onChanged: val => { config.DelayAfterEnd = val; },
text: LocalizationManager.Get("ui.render_config.delay_after_end"),
id: "slider_delay_after_end", showFill: true
); );
_showJudgementToggle = CreateToggleField( _showJudgementToggle = CreateToggleField(
ctx, CreateFieldSlot(topRight), ctx, CreateFieldSlot(topRight), "",
LocalizationManager.Get("ui.render_config.show_judgement"),
false, config.ShowJudgementText, false, config.ShowJudgementText,
LocalizationManager.Get("ui.render_config.show_judgement"), LocalizationManager.Get("ui.render_config.show_judgement"),
"toggle_show_judgement", "toggle_show_judgement",
val => config.ShowJudgementText = val val => config.ShowJudgementText = val,
generateLabel: false
); );
// 4. Video Encoding Config (Collapsible Card) // 4. Video Encoding Config (Collapsible Card)
@@ -192,21 +224,25 @@ internal static class RenderConfigWindow
TMPro.TMP_InputField.CharacterValidation.Integer, TMPro.TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var mb) && mb > 0) config.VideoConfig.MaxBitrate = mb; } val => { if (int.TryParse(val, out var mb) && mb > 0) config.VideoConfig.MaxBitrate = mb; }
); );
_keyframeIntervalInput = CreateInputField(
ctx, CreateFieldSlot(videoRight), _forceSoftwareToggle = CreateToggleField(
LocalizationManager.Get("ui.render_config.keyframe_interval"), ctx, CreateFieldSlot(videoRight), "",
"2.0", config.VideoConfig.KeyframeInterval.ToString("0.##"), true, config.VideoConfig.ForceSoftwareEncoder,
"input_keyframe_interval", LocalizationManager.Get("ui.render_config.force_software"),
TMPro.TMP_InputField.CharacterValidation.Decimal, "toggle_force_software",
val => { if (double.TryParse(val, out var kf) && kf > 0) config.VideoConfig.KeyframeInterval = kf; } val => config.AudioConfig.RenderAudio = val,
generateLabel: true
); );
_forceSoftwareToggle = O5Factory.Toggle( _keyframeIntervalSlider = O5Factory.Slider(
ctx, videoContent, ctx: ctx, parent: videoContent,
false, config.VideoConfig.ForceSoftwareEncoder, defaultValue: 2, min: 0.1, max: 10, value: 2,
val => config.VideoConfig.ForceSoftwareEncoder = val, format: "F1", clampMode: ClampMode.All,
LocalizationManager.Get("ui.render_config.force_software"),
"toggle_force_software" sliderFilter: null, inputFilter: null,
onComplete: null, onChanged: val => { config.VideoConfig.KeyframeInterval = val; },
text: LocalizationManager.Get("ui.render_config.keyframe_interval"),
id: "slider_keyframe_interval", showFill: true
); );
// 5. Audio Encoding Config (Collapsible Card) // 5. Audio Encoding Config (Collapsible Card)
@@ -222,13 +258,25 @@ internal static class RenderConfigWindow
var (audioLeft, audioRight) = CreateTwoColumnPanel(audioContent); var (audioLeft, audioRight) = CreateTwoColumnPanel(audioContent);
_renderAudioToggle = CreateToggleField( _renderAudioToggle = CreateToggleField(
ctx, CreateFieldSlot(audioLeft), ctx, CreateFieldSlot(audioLeft), "",
LocalizationManager.Get("ui.render_config.render_audio"),
true, config.AudioConfig.RenderAudio, true, config.AudioConfig.RenderAudio,
LocalizationManager.Get("ui.render_config.render_audio"), LocalizationManager.Get("ui.render_config.render_audio"),
"toggle_render_audio", "toggle_render_audio",
val => config.AudioConfig.RenderAudio = val val => config.AudioConfig.RenderAudio = val,
generateLabel: false
); );
_audioBitrateSlider = O5Factory.Slider(
ctx: ctx, parent: CreateFieldSlot(audioRight),
defaultValue: 256, min: 64, max: 384, value: 256,
format: "F0", clampMode: ClampMode.All,
sliderFilter: value => audioBitrateSnap.OrderBy(n => Math.Abs(n - value)).First(),
inputFilter: null, onComplete: null, onChanged: val => { config.AudioConfig.AudioBitrate = (int)Math.Round(val) * 1000; },
text: LocalizationManager.Get("ui.render_config.audio_bitrate"),
id: "slider_audio_bitrate", showFill: true
);
var channels = new[] { 1, 2 }; var channels = new[] { 1, 2 };
_channelsDropdown = CreateDropdownField( _channelsDropdown = CreateDropdownField(
ctx, CreateFieldSlot(audioLeft), ctx, CreateFieldSlot(audioLeft),
@@ -239,23 +287,15 @@ internal static class RenderConfigWindow
c => config.AudioConfig.AudioChannels = c c => config.AudioConfig.AudioChannels = c
); );
var sampleRates = new[] { 44100, 48000 }; var sampleRates = new[] { 44100, 48000, 96000 };
_sampleRateDropdown = CreateDropdownField( _sampleRateDropdown = CreateDropdownField(
ctx, CreateFieldSlot(audioRight), ctx, CreateFieldSlot(audioRight),
LocalizationManager.Get("ui.render_config.sample_rate"), LocalizationManager.Get("ui.render_config.sample_rate"),
44100, config.AudioConfig.AudioSampleRate, 48000, config.AudioConfig.AudioSampleRate,
sampleRates, r => $"{r} Hz", sampleRates, r => $"{r/1000.0} kHz",
"dd_sample_rate", "dd_sample_rate",
r => config.AudioConfig.AudioSampleRate = r r => config.AudioConfig.AudioSampleRate = r
); );
_audioBitrateInput = CreateInputField(
ctx, CreateFieldSlot(audioRight),
LocalizationManager.Get("ui.render_config.audio_bitrate"),
"256", (config.AudioConfig.AudioBitrate / 1000).ToString(),
"input_audio_bitrate",
TMPro.TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var ab) && ab > 0) config.AudioConfig.AudioBitrate = ab * 1000; }
);
// 6. Global Settings (Collapsible Card) // 6. Global Settings (Collapsible Card)
var (_, globalContent) = O5Factory.Card( var (_, globalContent) = O5Factory.Card(
@@ -345,12 +385,12 @@ internal static class RenderConfigWindow
e => { if (!string.IsNullOrEmpty(e)) config.AdvancedConfig.FileExtension = e; } e => { if (!string.IsNullOrEmpty(e)) config.AdvancedConfig.FileExtension = e; }
); );
_processAudioToggle = CreateToggleField( _processAudioToggle = CreateToggleField(
ctx, CreateFieldSlot(advLeft), ctx, CreateFieldSlot(advLeft), "",
LocalizationManager.Get("ui.render_config.process_audio"),
true, config.AdvancedConfig.ProcessAudio, true, config.AdvancedConfig.ProcessAudio,
LocalizationManager.Get("ui.render_config.process_audio"), LocalizationManager.Get("ui.render_config.process_audio"),
"toggle_process_audio", "toggle_process_audio",
val => config.AdvancedConfig.ProcessAudio = val val => config.AdvancedConfig.ProcessAudio = val,
generateLabel: false
); );
_randomSeedInput = CreateInputField( _randomSeedInput = CreateInputField(
@@ -366,12 +406,12 @@ internal static class RenderConfigWindow
} }
); );
_nonTileToggle = CreateToggleField( _nonTileToggle = CreateToggleField(
ctx, CreateFieldSlot(advRight), ctx, CreateFieldSlot(advRight), "",
LocalizationManager.Get("ui.render_config.non_tile"),
false, config.AdvancedConfig.NonTile, false, config.AdvancedConfig.NonTile,
LocalizationManager.Get("ui.render_config.non_tile"), LocalizationManager.Get("ui.render_config.non_tile"),
"toggle_non_tile", "toggle_non_tile",
val => config.AdvancedConfig.NonTile = val val => config.AdvancedConfig.NonTile = val,
generateLabel: false
); );
// 8. Go Render Button // 8. Go Render Button
@@ -386,22 +426,22 @@ internal static class RenderConfigWindow
private static void EnsureDefaultConfig(RenderConfig config) private static void EnsureDefaultConfig(RenderConfig config)
{ {
if (config.RenderWidth <= 0) config.RenderWidth = Screen.width > 0 ? Screen.width : 1920; if (config.RenderWidth <= 0) config.RenderWidth = Screen.width > 0 ? Screen.width : 2560;
if (config.RenderHeight <= 0) config.RenderHeight = Screen.height > 0 ? Screen.height : 1080; if (config.RenderHeight <= 0) config.RenderHeight = Screen.height > 0 ? Screen.height : 1440;
if (config.RenderFPS <= 0) config.RenderFPS = 60.0; if (config.RenderFPS <= 0) config.RenderFPS = 60.0;
if (config.VideoFPS <= 0) config.VideoFPS = 60.0; if (config.VideoFPS <= 0) config.VideoFPS = 60.0;
if (config.DelayAfterEnd < 0) config.DelayAfterEnd = 1.0; if (config.DelayAfterEnd < 0) config.DelayAfterEnd = 15.0;
} }
private static void ApplyConfigToUI(RenderConfig config) private static void ApplyConfigToUI(RenderConfig config)
{ {
EnsureDefaultConfig(config); EnsureDefaultConfig(config);
_renderFpsInput?.Set(config.RenderFPS.ToString("0.##"), false); _renderFpsSlider?.Set((float) config.RenderFPS, false);
_videoFpsInput?.Set(config.VideoFPS.ToString("0.##"), false); _videoFPSSlider?.Set((float) config.VideoFPS, false);
_renderWidthInput?.Set(config.RenderWidth.ToString(), false); _renderWidthSlider?.Set(config.RenderWidth, false);
_renderHeightInput?.Set(config.RenderHeight.ToString(), false); _renderHeightSlider?.Set(config.RenderHeight, false);
_delayAfterEndInput?.Set(config.DelayAfterEnd.ToString("0.##"), false); _delayAfterEndSlider?.Set((float) config.DelayAfterEnd, false);
_showJudgementToggle?.Set(config.ShowJudgementText, false); _showJudgementToggle?.Set(config.ShowJudgementText, false);
_codecDropdown?.Set(config.VideoConfig.Codec, false); _codecDropdown?.Set(config.VideoConfig.Codec, false);
@@ -409,13 +449,13 @@ internal static class RenderConfigWindow
_bitrateInput?.Set(config.VideoConfig.Bitrate.ToString(), false); _bitrateInput?.Set(config.VideoConfig.Bitrate.ToString(), false);
_maxBitrateInput?.Set(config.VideoConfig.MaxBitrate.ToString(), false); _maxBitrateInput?.Set(config.VideoConfig.MaxBitrate.ToString(), false);
_qpInput?.Set(config.VideoConfig.QP.ToString(), false); _qpInput?.Set(config.VideoConfig.QP.ToString(), false);
_keyframeIntervalInput?.Set(config.VideoConfig.KeyframeInterval.ToString("0.##"), false); _keyframeIntervalSlider?.Set(config.VideoConfig.KeyframeInterval, false);
_forceSoftwareToggle?.Set(config.VideoConfig.ForceSoftwareEncoder, false); _forceSoftwareToggle?.Set(config.VideoConfig.ForceSoftwareEncoder, false);
_renderAudioToggle?.Set(config.AudioConfig.RenderAudio, false); _renderAudioToggle?.Set(config.AudioConfig.RenderAudio, false);
_sampleRateDropdown?.Set(config.AudioConfig.AudioSampleRate, false); _sampleRateDropdown?.Set(config.AudioConfig.AudioSampleRate, false);
_channelsDropdown?.Set(config.AudioConfig.AudioChannels, false); _channelsDropdown?.Set(config.AudioConfig.AudioChannels, false);
_audioBitrateInput?.Set((config.AudioConfig.AudioBitrate / 1000).ToString(), false); _audioBitrateSlider?.Set(config.AudioConfig.AudioBitrate / 1000.0, false);
_outputDirInput?.Set(GlobalSettings.EffectiveOutputDirectory, false); _outputDirInput?.Set(GlobalSettings.EffectiveOutputDirectory, false);
@@ -429,46 +469,6 @@ internal static class RenderConfigWindow
{ {
var config = RenderManager.Instance.Configuration; var config = RenderManager.Instance.Configuration;
// Validate RenderFPS
if (!double.TryParse(_renderFpsInput?.Value, out var renderFps) || renderFps <= 0)
{
ShowValidationError("Render FPS must be a positive number.");
return;
}
config.RenderFPS = renderFps;
// Validate VideoFPS
if (!double.TryParse(_videoFpsInput?.Value, out var videoFps) || videoFps <= 0)
{
ShowValidationError("Video FPS must be a positive number.");
return;
}
config.VideoFPS = videoFps;
// Validate RenderWidth
if (!int.TryParse(_renderWidthInput?.Value, out var renderWidth) || renderWidth <= 0)
{
ShowValidationError("Render Width must be an integer greater than 0.");
return;
}
config.RenderWidth = renderWidth;
// Validate RenderHeight
if (!int.TryParse(_renderHeightInput?.Value, out var renderHeight) || renderHeight <= 0)
{
ShowValidationError("Render Height must be an integer greater than 0.");
return;
}
config.RenderHeight = renderHeight;
// Validate DelayAfterEnd
if (!double.TryParse(_delayAfterEndInput?.Value, out var delay) || delay < 0)
{
ShowValidationError("Delay After End must be a non-negative number.");
return;
}
config.DelayAfterEnd = delay;
// Sync Video Config // Sync Video Config
if (_bitrateInput != null && int.TryParse(_bitrateInput.Value, out var bitrate) && bitrate > 0) if (_bitrateInput != null && int.TryParse(_bitrateInput.Value, out var bitrate) && bitrate > 0)
{ {
@@ -482,16 +482,6 @@ internal static class RenderConfigWindow
{ {
config.VideoConfig.QP = qp; config.VideoConfig.QP = qp;
} }
if (_keyframeIntervalInput != null && double.TryParse(_keyframeIntervalInput.Value, out var kf) && kf > 0)
{
config.VideoConfig.KeyframeInterval = kf;
}
// Sync Audio Config
if (_audioBitrateInput != null && int.TryParse(_audioBitrateInput.Value, out var ab) && ab > 0)
{
config.AudioConfig.AudioBitrate = ab * 1000;
}
// Sync Advanced Config // Sync Advanced Config
if (_randomSeedInput != null) if (_randomSeedInput != null)
@@ -573,6 +563,7 @@ internal static class RenderConfigWindow
hLayout.childForceExpandWidth = true; hLayout.childForceExpandWidth = true;
hLayout.childForceExpandHeight = false; hLayout.childForceExpandHeight = false;
// ReSharper disable once InconsistentNaming
var panelLE = panelObj.AddComponent<LayoutElement>(); var panelLE = panelObj.AddComponent<LayoutElement>();
panelLE.flexibleWidth = 1f; panelLE.flexibleWidth = 1f;
@@ -596,6 +587,7 @@ internal static class RenderConfigWindow
colLayout.childForceExpandWidth = true; colLayout.childForceExpandWidth = true;
colLayout.childForceExpandHeight = false; colLayout.childForceExpandHeight = false;
// ReSharper disable once InconsistentNaming
// 핵심: 좌/우 선호 너비를 0으로 강제해 항상 정확히 절반씩 나누도록 한다. // 핵심: 좌/우 선호 너비를 0으로 강제해 항상 정확히 절반씩 나누도록 한다.
// (기존 행마다 HLG 방식은 라벨/값 텍스트 길이에 따라 preferred가 달라져 // (기존 행마다 HLG 방식은 라벨/값 텍스트 길이에 따라 preferred가 달라져
// 중앙이 (W + P1 - P2) / 2 로 행마다 틀어졌음) // 중앙이 (W + P1 - P2) / 2 로 행마다 틀어졌음)
@@ -621,6 +613,7 @@ internal static class RenderConfigWindow
slotLayout.childForceExpandWidth = true; slotLayout.childForceExpandWidth = true;
slotLayout.childForceExpandHeight = false; slotLayout.childForceExpandHeight = false;
// ReSharper disable once InconsistentNaming
var slotLE = slotObj.AddComponent<LayoutElement>(); var slotLE = slotObj.AddComponent<LayoutElement>();
slotLE.flexibleWidth = 1f; slotLE.flexibleWidth = 1f;
@@ -712,16 +705,20 @@ internal static class RenderConfigWindow
bool value, bool value,
string toggleText, string toggleText,
string id, string id,
Action<bool>? onChanged = null) Action<bool>? onChanged = null,
bool generateLabel = true)
{ {
var label = O5Factory.ControlText(ctx, colParent, 13f, noPad: true); if (generateLabel)
label.text = labelText; {
label.color = ctx.Theme.TextDim; var label = O5Factory.ControlText(ctx, colParent, 13f, noPad: true);
label.text = labelText;
// ReSharper disable once InconsistentNaming label.color = ctx.Theme.TextDim;
var labelLE = label.gameObject.AddComponent<LayoutElement>();
labelLE.preferredHeight = 18f; // ReSharper disable once InconsistentNaming
labelLE.minHeight = 18f; var labelLE = label.gameObject.AddComponent<LayoutElement>();
labelLE.preferredHeight = 18f;
labelLE.minHeight = 18f;
}
return O5Factory.Toggle( return O5Factory.Toggle(
ctx: ctx, ctx: ctx,
@@ -756,12 +753,11 @@ internal static class RenderConfigWindow
var ddContainer = new GameObject($"DD_{id}"); var ddContainer = new GameObject($"DD_{id}");
ddContainer.transform.SetParent(colParent, false); ddContainer.transform.SetParent(colParent, false);
var ddRect = ddContainer.AddComponent<RectTransform>();
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
var ddLE = ddContainer.AddComponent<LayoutElement>(); var ddLE = ddContainer.AddComponent<LayoutElement>();
ddLE.preferredHeight = 50f; ddLE.preferredHeight = 50f;
ddLE.minHeight = 50f; ddLE.minHeight = 0f;
return O5Factory.DropDown( return O5Factory.DropDown(
ctx: ctx, ctx: ctx,