bugfixes
This commit is contained in:
@@ -60,6 +60,7 @@ namespace SplashEdit.EditorCode
|
||||
private const int FUNC_SEEK = 0x107;
|
||||
|
||||
public bool IsRunning => _listenTask != null && !_listenTask.IsCompleted;
|
||||
public bool HasError { get; private set; }
|
||||
|
||||
public PCdrvSerialHost(string portName, int baudRate, string baseDir, Action<string> log, Action<string> psxLog = null)
|
||||
{
|
||||
@@ -157,6 +158,7 @@ namespace SplashEdit.EditorCode
|
||||
bool lastByteWasEscape = false;
|
||||
var textBuffer = new StringBuilder();
|
||||
int totalBytesReceived = 0;
|
||||
int consecutiveErrors = 0;
|
||||
DateTime lastLogTime = DateTime.Now;
|
||||
|
||||
_log?.Invoke("PCdrv monitor: waiting for data from PS1...");
|
||||
@@ -179,6 +181,7 @@ namespace SplashEdit.EditorCode
|
||||
}
|
||||
|
||||
int b = _port.ReadByte();
|
||||
consecutiveErrors = 0;
|
||||
totalBytesReceived++;
|
||||
|
||||
// Log first bytes received to help diagnose protocol issues
|
||||
@@ -256,8 +259,16 @@ namespace SplashEdit.EditorCode
|
||||
catch (OperationCanceledException) { break; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!ct.IsCancellationRequested)
|
||||
_log?.Invoke($"PCdrv monitor error: {ex.Message}");
|
||||
if (ct.IsCancellationRequested) break;
|
||||
consecutiveErrors++;
|
||||
_log?.Invoke($"PCdrv monitor error: {ex.Message}");
|
||||
if (consecutiveErrors >= 3)
|
||||
{
|
||||
_log?.Invoke("PCdrv host: too many errors, connection lost. Stopping.");
|
||||
HasError = true;
|
||||
break;
|
||||
}
|
||||
Thread.Sleep(100); // Back off before retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,9 +247,25 @@ namespace SplashEdit.EditorCode
|
||||
return tex;
|
||||
}
|
||||
|
||||
// Snapshot taken at the start of each OnGUI so Layout and Repaint
|
||||
// events always see the same line count (prevents "Getting control
|
||||
// position in a group with only N controls" errors).
|
||||
private LogLine[] _snapshot = Array.Empty<LogLine>();
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EnsureStyles();
|
||||
|
||||
// Take a snapshot once per OnGUI so Layout and Repaint see
|
||||
// identical control counts even if background threads add lines.
|
||||
if (Event.current.type == EventType.Layout)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_snapshot = _lines.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
DrawToolbar();
|
||||
DrawConsoleOutput();
|
||||
}
|
||||
@@ -310,69 +326,70 @@ namespace SplashEdit.EditorCode
|
||||
int selMax = Mathf.Max(_selectionAnchor, _selectionEnd);
|
||||
bool hasSelection = _selectionAnchor >= 0 && _selectionEnd >= 0;
|
||||
|
||||
lock (_lock)
|
||||
// Iterate the snapshot taken during Layout so the control count
|
||||
// is stable across Layout and Repaint events.
|
||||
var snapshot = _snapshot;
|
||||
|
||||
if (snapshot.Length == 0)
|
||||
{
|
||||
if (_lines.Count == 0)
|
||||
GUILayout.Label("Waiting for output...", EditorStyles.centeredGreyMiniLabel);
|
||||
}
|
||||
|
||||
for (int i = 0; i < snapshot.Length; i++)
|
||||
{
|
||||
var line = snapshot[i];
|
||||
|
||||
if (line.isError && !_showStderr) continue;
|
||||
if (!line.isError && !_showStdout) continue;
|
||||
if (hasFilter && line.text.ToLowerInvariant().IndexOf(filterLower, StringComparison.Ordinal) < 0)
|
||||
continue;
|
||||
|
||||
bool selected = hasSelection && i >= selMin && i <= selMax;
|
||||
GUIStyle style = selected ? _monoStyleSelected : (line.isError ? _monoStyleErr : _monoStyle);
|
||||
|
||||
string label = $"[{line.timestamp}] {line.text}";
|
||||
GUILayout.Label(label, style);
|
||||
|
||||
// Handle click/right-click on last drawn rect
|
||||
Rect lineRect = GUILayoutUtility.GetLastRect();
|
||||
Event evt = Event.current;
|
||||
if (evt.type == EventType.MouseDown && lineRect.Contains(evt.mousePosition))
|
||||
{
|
||||
GUILayout.Label("Waiting for output...", EditorStyles.centeredGreyMiniLabel);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _lines.Count; i++)
|
||||
{
|
||||
var line = _lines[i];
|
||||
|
||||
if (line.isError && !_showStderr) continue;
|
||||
if (!line.isError && !_showStdout) continue;
|
||||
if (hasFilter && line.text.ToLowerInvariant().IndexOf(filterLower, StringComparison.Ordinal) < 0)
|
||||
continue;
|
||||
|
||||
bool selected = hasSelection && i >= selMin && i <= selMax;
|
||||
GUIStyle style = selected ? _monoStyleSelected : (line.isError ? _monoStyleErr : _monoStyle);
|
||||
|
||||
string label = $"[{line.timestamp}] {line.text}";
|
||||
GUILayout.Label(label, style);
|
||||
|
||||
// Handle click/right-click on last drawn rect
|
||||
Rect lineRect = GUILayoutUtility.GetLastRect();
|
||||
Event evt = Event.current;
|
||||
if (evt.type == EventType.MouseDown && lineRect.Contains(evt.mousePosition))
|
||||
if (evt.button == 0)
|
||||
{
|
||||
if (evt.button == 0)
|
||||
if (evt.shift && _selectionAnchor >= 0)
|
||||
_selectionEnd = i;
|
||||
else
|
||||
{
|
||||
if (evt.shift && _selectionAnchor >= 0)
|
||||
_selectionEnd = i;
|
||||
else
|
||||
{
|
||||
_selectionAnchor = i;
|
||||
_selectionEnd = i;
|
||||
}
|
||||
evt.Use();
|
||||
Repaint();
|
||||
_selectionAnchor = i;
|
||||
_selectionEnd = i;
|
||||
}
|
||||
else if (evt.button == 1)
|
||||
evt.Use();
|
||||
Repaint();
|
||||
}
|
||||
else if (evt.button == 1)
|
||||
{
|
||||
int clickedLine = i;
|
||||
bool lineInSelection = hasSelection && clickedLine >= selMin && clickedLine <= selMax;
|
||||
var menu = new GenericMenu();
|
||||
if (lineInSelection && selMin != selMax)
|
||||
{
|
||||
int clickedLine = i;
|
||||
bool lineInSelection = hasSelection && clickedLine >= selMin && clickedLine <= selMax;
|
||||
var menu = new GenericMenu();
|
||||
if (lineInSelection && selMin != selMax)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Copy selected lines"), false, () => CopyRange(selMin, selMax));
|
||||
menu.AddSeparator("");
|
||||
}
|
||||
menu.AddItem(new GUIContent("Copy this line"), false, () =>
|
||||
{
|
||||
string text;
|
||||
lock (_lock)
|
||||
{
|
||||
text = clickedLine < _lines.Count
|
||||
? $"[{_lines[clickedLine].timestamp}] {_lines[clickedLine].text}"
|
||||
: "";
|
||||
}
|
||||
EditorGUIUtility.systemCopyBuffer = text;
|
||||
});
|
||||
menu.ShowAsContext();
|
||||
evt.Use();
|
||||
menu.AddItem(new GUIContent("Copy selected lines"), false, () => CopyRange(selMin, selMax));
|
||||
menu.AddSeparator("");
|
||||
}
|
||||
menu.AddItem(new GUIContent("Copy this line"), false, () =>
|
||||
{
|
||||
string text;
|
||||
lock (_lock)
|
||||
{
|
||||
text = clickedLine < _lines.Count
|
||||
? $"[{_lines[clickedLine].timestamp}] {_lines[clickedLine].text}"
|
||||
: "";
|
||||
}
|
||||
EditorGUIUtility.systemCopyBuffer = text;
|
||||
});
|
||||
menu.ShowAsContext();
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
// ───── Constants ─────
|
||||
private const string WINDOW_TITLE = "SplashEdit Control Panel";
|
||||
private const string MENU_PATH = "PlayStation 1/SplashEdit Control Panel %#p";
|
||||
private const string MENU_PATH = "PlayStation 1/SplashEdit Control Panel %#l";
|
||||
|
||||
// ───── UI State ─────
|
||||
private Vector2 _scrollPos;
|
||||
private bool _showQuickStart = true;
|
||||
private int _selectedTab = 0;
|
||||
private static readonly string[] _tabNames = { "Dependencies", "Scenes", "Build" };
|
||||
private bool _showNativeProject = true;
|
||||
private bool _showToolchainSection = true;
|
||||
private bool _showScenesSection = true;
|
||||
@@ -84,12 +85,10 @@ namespace SplashEdit.EditorCode
|
||||
RefreshToolchainStatus();
|
||||
LoadSceneList();
|
||||
_manualNativePath = SplashSettings.NativeProjectPath;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeChanged;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeChanged;
|
||||
}
|
||||
|
||||
private void OnFocus()
|
||||
@@ -97,61 +96,40 @@ namespace SplashEdit.EditorCode
|
||||
RefreshToolchainStatus();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Play Mode Intercept
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void OnPlayModeChanged(PlayModeStateChange state)
|
||||
{
|
||||
if (state == PlayModeStateChange.ExitingEditMode && SplashSettings.InterceptPlayMode)
|
||||
{
|
||||
EditorApplication.isPlaying = false;
|
||||
Log("Play Mode intercepted — starting Build & Run instead.", LogType.Log);
|
||||
BuildAndRun();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Main GUI
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
|
||||
if (_isRunning && _pcdrvHost != null && !_pcdrvHost.IsRunning)
|
||||
{
|
||||
StopAll();
|
||||
Log("PCdrv host connection lost.", LogType.Warning);
|
||||
}
|
||||
|
||||
DrawHeader();
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Show Quick Start prominently if toolchain is not ready
|
||||
if (!_hasMIPS || !_hasNativeProject)
|
||||
_selectedTab = PSXEditorStyles.DrawButtonGroup(_tabNames, _selectedTab, 28);
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
|
||||
|
||||
switch (_selectedTab)
|
||||
{
|
||||
DrawQuickStartSection();
|
||||
EditorGUILayout.Space(2);
|
||||
case 0: // Dependencies
|
||||
DrawToolchainSection();
|
||||
EditorGUILayout.Space(2);
|
||||
DrawNativeProjectSection();
|
||||
break;
|
||||
case 1: // Scenes
|
||||
DrawScenesSection();
|
||||
break;
|
||||
case 2: // Build
|
||||
DrawBuildSection();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Collapsed quick start for experienced users
|
||||
_showQuickStart = DrawSectionFoldout("Quick Start Guide", _showQuickStart);
|
||||
if (_showQuickStart)
|
||||
{
|
||||
DrawQuickStartContent();
|
||||
}
|
||||
EditorGUILayout.Space(2);
|
||||
}
|
||||
|
||||
DrawNativeProjectSection();
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
DrawToolchainSection();
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
DrawScenesSection();
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
DrawVRAMSection();
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
DrawBuildSection();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
@@ -167,19 +145,9 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.Label("SplashEdit", PSXEditorStyles.WindowHeader);
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// Play mode intercept toggle
|
||||
bool intercept = SplashSettings.InterceptPlayMode;
|
||||
var toggleContent = new GUIContent(
|
||||
intercept ? "▶ Intercept ON" : "▶ Intercept OFF",
|
||||
"When enabled, pressing Play in Unity triggers Build & Run instead.");
|
||||
bool newIntercept = GUILayout.Toggle(intercept, toggleContent, EditorStyles.toolbarButton, GUILayout.Width(120));
|
||||
if (newIntercept != intercept)
|
||||
SplashSettings.InterceptPlayMode = newIntercept;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// Status bar
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
|
||||
{
|
||||
string statusText;
|
||||
Color statusColor;
|
||||
@@ -210,90 +178,13 @@ namespace SplashEdit.EditorCode
|
||||
statusColor = PSXEditorStyles.Success;
|
||||
}
|
||||
|
||||
var prevColor = GUI.contentColor;
|
||||
GUI.contentColor = statusColor;
|
||||
GUILayout.Label(statusText, EditorStyles.miniLabel);
|
||||
GUI.contentColor = prevColor;
|
||||
EditorGUILayout.BeginHorizontal(PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.DrawStatusBadge(statusColor == PSXEditorStyles.Success ? "OK" :
|
||||
statusColor == PSXEditorStyles.Warning ? "WARN" :
|
||||
statusColor == PSXEditorStyles.Info ? "INFO" : "RUN", statusColor, 50);
|
||||
GUILayout.Label(statusText, PSXEditorStyles.RichLabel);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Quick Start Guide
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void DrawQuickStartSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
|
||||
|
||||
var prevColor = GUI.contentColor;
|
||||
GUI.contentColor = PSXEditorStyles.AccentGold;
|
||||
GUILayout.Label("Getting Started with SplashEdit", PSXEditorStyles.CardHeaderStyle);
|
||||
GUI.contentColor = prevColor;
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
DrawQuickStartContent();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawQuickStartContent()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
DrawQuickStartStep(1, "Install Toolchain",
|
||||
"Install the MIPS cross-compiler and GNU Make below.",
|
||||
_hasMIPS && _hasMake);
|
||||
|
||||
DrawQuickStartStep(2, "Get Native Project",
|
||||
"Clone the psxsplash runtime or set a path to your local copy.",
|
||||
_hasNativeProject);
|
||||
|
||||
DrawQuickStartStep(3, "Add Scenes",
|
||||
"Add Unity scenes containing a PSXSceneExporter to the scene list.",
|
||||
_sceneList.Count > 0);
|
||||
|
||||
DrawQuickStartStep(4, "Configure VRAM",
|
||||
"Set the framebuffer resolution and texture packing settings.",
|
||||
true); // Always "done" since defaults are fine
|
||||
|
||||
DrawQuickStartStep(5, "Build & Run",
|
||||
"Click BUILD & RUN to export, compile, and launch on the emulator or real hardware.",
|
||||
false);
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("Open Documentation", EditorStyles.miniButton, GUILayout.Width(140)))
|
||||
{
|
||||
Application.OpenURL("https://github.com/psxsplash/splashedit");
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawQuickStartStep(int step, string title, string description, bool done)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// Checkbox/step indicator
|
||||
string prefix = done ? "✓" : $"{step}.";
|
||||
var style = done ? EditorStyles.miniLabel : EditorStyles.boldLabel;
|
||||
|
||||
var prevColor = GUI.contentColor;
|
||||
GUI.contentColor = done ? PSXEditorStyles.Success : PSXEditorStyles.TextPrimary;
|
||||
GUILayout.Label(prefix, style, GUILayout.Width(20));
|
||||
GUI.contentColor = prevColor;
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Label(title, EditorStyles.boldLabel);
|
||||
GUILayout.Label(description, EditorStyles.wordWrappedMiniLabel);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space(2);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
@@ -327,7 +218,8 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.Space(6);
|
||||
|
||||
// ── Option 1: Auto-clone from GitHub ──
|
||||
EditorGUILayout.LabelField("Clone from GitHub", EditorStyles.boldLabel);
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
GUILayout.Label("Clone from GitHub", PSXEditorStyles.SectionHeader);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label(PSXSplashInstaller.RepoUrl, EditorStyles.miniLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
@@ -341,7 +233,7 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
if (_isInstallingNative)
|
||||
{
|
||||
EditorGUILayout.HelpBox(_nativeInstallStatus, MessageType.Info);
|
||||
GUILayout.Label(_nativeInstallStatus, PSXEditorStyles.InfoBox);
|
||||
}
|
||||
|
||||
// If already cloned, show version management
|
||||
@@ -362,7 +254,8 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.Space(6);
|
||||
|
||||
// ── Option 2: Manual path ──
|
||||
EditorGUILayout.LabelField("Or set path manually", EditorStyles.boldLabel);
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
GUILayout.Label("Or set path manually", PSXEditorStyles.SectionHeader);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
string newPath = EditorGUILayout.TextField(_manualNativePath);
|
||||
@@ -389,7 +282,7 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (!string.IsNullOrEmpty(_manualNativePath) && !manualPathValid)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Invalid path. The directory must contain a Makefile.", MessageType.Warning);
|
||||
GUILayout.Label("Invalid path. The directory must contain a Makefile.", PSXEditorStyles.InfoBox);
|
||||
}
|
||||
else if (manualPathValid && _manualNativePath != SplashSettings.NativeProjectPath)
|
||||
{
|
||||
@@ -431,15 +324,17 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.FlexibleSpace();
|
||||
if (!_hasMIPS)
|
||||
{
|
||||
if (GUILayout.Button("Install", GUILayout.Width(60)))
|
||||
if (GUILayout.Button("Install", PSXEditorStyles.SecondaryButton, GUILayout.Width(70)))
|
||||
InstallMIPS();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("Ready", EditorStyles.miniLabel);
|
||||
PSXEditorStyles.DrawStatusBadge("Ready", PSXEditorStyles.Success);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
PSXEditorStyles.DrawSeparator(2, 2);
|
||||
|
||||
// GNU Make
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawStatusIcon(_hasMake);
|
||||
@@ -447,15 +342,17 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.FlexibleSpace();
|
||||
if (!_hasMake)
|
||||
{
|
||||
if (GUILayout.Button("Install", GUILayout.Width(60)))
|
||||
if (GUILayout.Button("Install", PSXEditorStyles.SecondaryButton, GUILayout.Width(70)))
|
||||
InstallMake();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("Ready", EditorStyles.miniLabel);
|
||||
PSXEditorStyles.DrawStatusBadge("Ready", PSXEditorStyles.Success);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
PSXEditorStyles.DrawSeparator(2, 2);
|
||||
|
||||
// PCSX-Redux
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawStatusIcon(_hasRedux);
|
||||
@@ -463,15 +360,17 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.FlexibleSpace();
|
||||
if (!_hasRedux)
|
||||
{
|
||||
if (GUILayout.Button("Download", GUILayout.Width(80)))
|
||||
if (GUILayout.Button("Download", PSXEditorStyles.SecondaryButton, GUILayout.Width(80)))
|
||||
DownloadRedux();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label(_reduxVersion, EditorStyles.miniLabel);
|
||||
PSXEditorStyles.DrawStatusBadge(_reduxVersion, PSXEditorStyles.Success);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
PSXEditorStyles.DrawSeparator(2, 2);
|
||||
|
||||
// psxavenc (audio encoder)
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawStatusIcon(_hasPsxavenc);
|
||||
@@ -479,15 +378,17 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.FlexibleSpace();
|
||||
if (!_hasPsxavenc)
|
||||
{
|
||||
if (GUILayout.Button("Download", GUILayout.Width(80)))
|
||||
if (GUILayout.Button("Download", PSXEditorStyles.SecondaryButton, GUILayout.Width(80)))
|
||||
DownloadPsxavenc();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("Installed", EditorStyles.miniLabel);
|
||||
PSXEditorStyles.DrawStatusBadge("Installed", PSXEditorStyles.Success);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
PSXEditorStyles.DrawSeparator(2, 2);
|
||||
|
||||
// mkpsxiso (ISO builder)
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawStatusIcon(_hasMkpsxiso);
|
||||
@@ -495,12 +396,12 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.FlexibleSpace();
|
||||
if (!_hasMkpsxiso)
|
||||
{
|
||||
if (GUILayout.Button("Download", GUILayout.Width(80)))
|
||||
if (GUILayout.Button("Download", PSXEditorStyles.SecondaryButton, GUILayout.Width(80)))
|
||||
DownloadMkpsxiso();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("Installed", EditorStyles.miniLabel);
|
||||
PSXEditorStyles.DrawStatusBadge("Installed", PSXEditorStyles.Success);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
@@ -528,11 +429,11 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
if (_sceneList.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
GUILayout.Label(
|
||||
"No scenes added yet.\n" +
|
||||
"Each scene needs a GameObject with a PSXSceneExporter component.\n" +
|
||||
"Drag scene assets here, or use the buttons below to add them.",
|
||||
MessageType.Info);
|
||||
PSXEditorStyles.InfoBox);
|
||||
}
|
||||
|
||||
// Draw scene list
|
||||
@@ -640,49 +541,19 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
|
||||
|
||||
// Resolution settings
|
||||
EditorGUILayout.LabelField("Framebuffer Settings", EditorStyles.boldLabel);
|
||||
// Framebuffer: hardcoded 320x240, vertical, dual-buffered
|
||||
GUILayout.Label("Framebuffer", PSXEditorStyles.SectionHeader);
|
||||
GUILayout.Label("Resolution: 320x240 (dual-buffered, vertical layout)", PSXEditorStyles.InfoBox);
|
||||
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
|
||||
GUILayout.Label("Advanced Tools", PSXEditorStyles.SectionHeader);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Resolution:", GUILayout.Width(75));
|
||||
SplashSettings.ResolutionWidth = EditorGUILayout.IntField(SplashSettings.ResolutionWidth, GUILayout.Width(50));
|
||||
GUILayout.Label("×", GUILayout.Width(14));
|
||||
SplashSettings.ResolutionHeight = EditorGUILayout.IntField(SplashSettings.ResolutionHeight, GUILayout.Width(50));
|
||||
|
||||
// Common resolutions dropdown
|
||||
if (GUILayout.Button("Presets", EditorStyles.miniButton, GUILayout.Width(55)))
|
||||
{
|
||||
var menu = new GenericMenu();
|
||||
menu.AddItem(new GUIContent("256×240"), false, () => { SplashSettings.ResolutionWidth = 256; SplashSettings.ResolutionHeight = 240; Repaint(); });
|
||||
menu.AddItem(new GUIContent("320×240"), false, () => { SplashSettings.ResolutionWidth = 320; SplashSettings.ResolutionHeight = 240; Repaint(); });
|
||||
menu.AddItem(new GUIContent("368×240"), false, () => { SplashSettings.ResolutionWidth = 368; SplashSettings.ResolutionHeight = 240; Repaint(); });
|
||||
menu.AddItem(new GUIContent("512×240"), false, () => { SplashSettings.ResolutionWidth = 512; SplashSettings.ResolutionHeight = 240; Repaint(); });
|
||||
menu.AddItem(new GUIContent("640×240"), false, () => { SplashSettings.ResolutionWidth = 640; SplashSettings.ResolutionHeight = 240; Repaint(); });
|
||||
menu.AddItem(new GUIContent("256×480"), false, () => { SplashSettings.ResolutionWidth = 256; SplashSettings.ResolutionHeight = 480; Repaint(); });
|
||||
menu.AddItem(new GUIContent("320×480"), false, () => { SplashSettings.ResolutionWidth = 320; SplashSettings.ResolutionHeight = 480; Repaint(); });
|
||||
menu.AddItem(new GUIContent("512×480"), false, () => { SplashSettings.ResolutionWidth = 512; SplashSettings.ResolutionHeight = 480; Repaint(); });
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
SplashSettings.DualBuffering = EditorGUILayout.Toggle("Dual Buffering", SplashSettings.DualBuffering);
|
||||
SplashSettings.VerticalLayout = EditorGUILayout.Toggle("Vertical Layout", SplashSettings.VerticalLayout);
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
EditorGUILayout.LabelField("Export Settings", EditorStyles.boldLabel);
|
||||
SplashSettings.DefaultGTEScaling = EditorGUILayout.FloatField("Default GTE Scaling", SplashSettings.DefaultGTEScaling);
|
||||
|
||||
EditorGUILayout.Space(6);
|
||||
|
||||
EditorGUILayout.LabelField("Advanced Tools", EditorStyles.boldLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Open VRAM Editor", GUILayout.Height(24)))
|
||||
if (GUILayout.Button("Open VRAM Editor", PSXEditorStyles.PrimaryButton, GUILayout.Height(26)))
|
||||
{
|
||||
VRAMEditorWindow.ShowWindow();
|
||||
}
|
||||
if (GUILayout.Button("Quantized Preview", GUILayout.Height(24)))
|
||||
if (GUILayout.Button("Quantized Preview", PSXEditorStyles.PrimaryButton, GUILayout.Height(26)))
|
||||
{
|
||||
QuantizedPreviewWindow.ShowWindow();
|
||||
}
|
||||
@@ -703,6 +574,7 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
|
||||
|
||||
// Target & Mode
|
||||
GUILayout.Label("Configuration", PSXEditorStyles.SectionHeader);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Target:", GUILayout.Width(50));
|
||||
SplashSettings.Target = (BuildTarget)EditorGUILayout.EnumPopup(SplashSettings.Target);
|
||||
@@ -710,6 +582,9 @@ namespace SplashEdit.EditorCode
|
||||
SplashSettings.Mode = (BuildMode)EditorGUILayout.EnumPopup(SplashSettings.Mode);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// Clean Build toggle
|
||||
SplashSettings.CleanBuild = EditorGUILayout.Toggle("Clean Build", SplashSettings.CleanBuild);
|
||||
|
||||
// Serial port (only for Real Hardware)
|
||||
if (SplashSettings.Target == BuildTarget.RealHardware)
|
||||
{
|
||||
@@ -749,53 +624,55 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(8);
|
||||
PSXEditorStyles.DrawSeparator(6, 6);
|
||||
|
||||
// Big Build & Run button
|
||||
EditorGUI.BeginDisabledGroup(_isBuilding);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
var buildColor = GUI.backgroundColor;
|
||||
GUI.backgroundColor = _isBuilding ? Color.gray : new Color(0.3f, 0.8f, 0.4f);
|
||||
var largeBuildButton = new GUIStyle(PSXEditorStyles.SuccessButton)
|
||||
{
|
||||
fontSize = 14,
|
||||
fontStyle = FontStyle.Bold,
|
||||
padding = new RectOffset(20, 20, 10, 10)
|
||||
};
|
||||
|
||||
string buttonLabel = _isBuilding ? "Building..." : "BUILD & RUN";
|
||||
if (GUILayout.Button(buttonLabel, GUILayout.Width(200), GUILayout.Height(36)))
|
||||
string buttonLabel = _isBuilding ? "Building..." :
|
||||
(SplashSettings.Target == BuildTarget.ISO ? "BUILD" : "BUILD & RUN");
|
||||
if (GUILayout.Button(buttonLabel, largeBuildButton, GUILayout.Width(200), GUILayout.Height(38)))
|
||||
{
|
||||
BuildAndRun();
|
||||
}
|
||||
|
||||
GUI.backgroundColor = buildColor;
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// Stop button (if running — emulator or hardware PCdrv host)
|
||||
// Stop button (if running - emulator or hardware PCdrv host)
|
||||
if (_isRunning)
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUI.backgroundColor = new Color(0.9f, 0.3f, 0.3f);
|
||||
string stopLabel = _emulatorProcess != null ? "■ STOP EMULATOR" : "■ STOP PCdrv HOST";
|
||||
if (GUILayout.Button(stopLabel, GUILayout.Width(200), GUILayout.Height(24)))
|
||||
string stopLabel = _emulatorProcess != null ? "STOP EMULATOR" : "STOP PCdrv HOST";
|
||||
if (GUILayout.Button(stopLabel, PSXEditorStyles.DangerButton, GUILayout.Width(200), GUILayout.Height(26)))
|
||||
{
|
||||
StopAll();
|
||||
}
|
||||
GUI.backgroundColor = buildColor;
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
// Export-only / Compile-only
|
||||
EditorGUILayout.Space(4);
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("Export Only", EditorStyles.miniButton, GUILayout.Width(100)))
|
||||
if (GUILayout.Button("Export Only", PSXEditorStyles.SecondaryButton, GUILayout.Width(100)))
|
||||
{
|
||||
ExportAllScenes();
|
||||
}
|
||||
if (GUILayout.Button("Compile Only", EditorStyles.miniButton, GUILayout.Width(100)))
|
||||
if (GUILayout.Button("Compile Only", PSXEditorStyles.SecondaryButton, GUILayout.Width(100)))
|
||||
{
|
||||
CompileOnly();
|
||||
}
|
||||
@@ -815,6 +692,20 @@ namespace SplashEdit.EditorCode
|
||||
public async void BuildAndRun()
|
||||
{
|
||||
if (_isBuilding) return;
|
||||
|
||||
if (UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().isDirty)
|
||||
{
|
||||
int choice = EditorUtility.DisplayDialogComplex(
|
||||
"Unsaved Changes",
|
||||
"The current scene has unsaved changes. Save before building?",
|
||||
"Save and Build", // 0
|
||||
"Cancel", // 1
|
||||
"Build Without Saving" // 2
|
||||
);
|
||||
if (choice == 1) return; // Cancel
|
||||
if (choice == 0) EditorSceneManager.SaveOpenScenes();
|
||||
}
|
||||
|
||||
_isBuilding = true;
|
||||
|
||||
var console = EditorWindow.GetWindow<PSXConsoleWindow>();
|
||||
@@ -1126,15 +1017,17 @@ namespace SplashEdit.EditorCode
|
||||
if (SplashSettings.Target == BuildTarget.ISO)
|
||||
buildArg += " LOADER=cdrom";
|
||||
|
||||
string makeCmd = $"make clean && make all -j{SystemInfo.processorCount} {buildArg}".Trim();
|
||||
int jobCount = Math.Max(1, SystemInfo.processorCount - 1);
|
||||
string cleanPrefix = SplashSettings.CleanBuild ? "make clean && " : "";
|
||||
string makeCmd = $"{cleanPrefix}make all -j{jobCount} {buildArg}".Trim();
|
||||
Log($"Running: {makeCmd}", LogType.Log);
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = Application.platform == RuntimePlatform.WindowsEditor ? "cmd.exe" : "/bin/bash",
|
||||
Arguments = Application.platform == RuntimePlatform.WindowsEditor
|
||||
? $"/c {makeCmd}"
|
||||
: $"-c \"{makeCmd}\"",
|
||||
? $"/c \"cd /d \"{nativeDir}\" && {makeCmd}\""
|
||||
: $"-c \"cd \\\"{nativeDir}\\\" && {makeCmd}\"",
|
||||
WorkingDirectory = nativeDir,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
@@ -1269,7 +1162,7 @@ namespace SplashEdit.EditorCode
|
||||
StopAllQuiet();
|
||||
|
||||
string pcdrvBase = SplashBuildPaths.BuildOutputDir;
|
||||
string args = $"-exe \"{exePath}\" -run -fastboot -pcdrv -pcdrvbase \"{pcdrvBase}\" -stdout";
|
||||
string args = $"-exe \"{exePath}\" -run -fastboot -pcdrv -pcdrvbase \"{pcdrvBase}\" -pad1type dualshock -stdout";
|
||||
|
||||
Log($"Launching: {Path.GetFileName(reduxPath)} {args}", LogType.Log);
|
||||
|
||||
@@ -1289,6 +1182,15 @@ namespace SplashEdit.EditorCode
|
||||
try
|
||||
{
|
||||
_emulatorProcess = Process.Start(psi);
|
||||
_emulatorProcess.EnableRaisingEvents = true;
|
||||
_emulatorProcess.Exited += (s, e) => {
|
||||
EditorApplication.delayCall += () => {
|
||||
_isRunning = false;
|
||||
_emulatorProcess = null;
|
||||
PSXConsoleWindow.Detach();
|
||||
Repaint();
|
||||
};
|
||||
};
|
||||
_isRunning = true;
|
||||
Log("PCSX-Redux launched.", LogType.Log);
|
||||
|
||||
|
||||
@@ -92,29 +92,36 @@ namespace SplashEdit.EditorCode
|
||||
set => EditorPrefs.SetInt(Prefix + "SerialBaudRate", value);
|
||||
}
|
||||
|
||||
// --- VRAM Layout ---
|
||||
// --- VRAM Layout (hardcoded 320x240, dual-buffered, vertical) ---
|
||||
public static int ResolutionWidth
|
||||
{
|
||||
get => EditorPrefs.GetInt(Prefix + "ResWidth", 320);
|
||||
set => EditorPrefs.SetInt(Prefix + "ResWidth", value);
|
||||
get => 320;
|
||||
set { } // no-op, hardcoded
|
||||
}
|
||||
|
||||
public static int ResolutionHeight
|
||||
{
|
||||
get => EditorPrefs.GetInt(Prefix + "ResHeight", 240);
|
||||
set => EditorPrefs.SetInt(Prefix + "ResHeight", value);
|
||||
get => 240;
|
||||
set { } // no-op, hardcoded
|
||||
}
|
||||
|
||||
public static bool DualBuffering
|
||||
{
|
||||
get => EditorPrefs.GetBool(Prefix + "DualBuffering", true);
|
||||
set => EditorPrefs.SetBool(Prefix + "DualBuffering", value);
|
||||
get => true;
|
||||
set { } // no-op, hardcoded
|
||||
}
|
||||
|
||||
public static bool VerticalLayout
|
||||
{
|
||||
get => EditorPrefs.GetBool(Prefix + "VerticalLayout", true);
|
||||
set => EditorPrefs.SetBool(Prefix + "VerticalLayout", value);
|
||||
get => true;
|
||||
set { } // no-op, hardcoded
|
||||
}
|
||||
|
||||
// --- Clean Build ---
|
||||
public static bool CleanBuild
|
||||
{
|
||||
get => EditorPrefs.GetBool(Prefix + "CleanBuild", true);
|
||||
set => EditorPrefs.SetBool(Prefix + "CleanBuild", value);
|
||||
}
|
||||
|
||||
// --- Export settings ---
|
||||
@@ -124,13 +131,6 @@ namespace SplashEdit.EditorCode
|
||||
set => EditorPrefs.SetFloat(Prefix + "GTEScaling", value);
|
||||
}
|
||||
|
||||
// --- Play Mode Intercept ---
|
||||
public static bool InterceptPlayMode
|
||||
{
|
||||
get => EditorPrefs.GetBool(Prefix + "InterceptPlayMode", false);
|
||||
set => EditorPrefs.SetBool(Prefix + "InterceptPlayMode", value);
|
||||
}
|
||||
|
||||
// --- ISO Build ---
|
||||
/// <summary>
|
||||
/// Optional path to a Sony license file (.dat) for the ISO image.
|
||||
@@ -162,7 +162,7 @@ namespace SplashEdit.EditorCode
|
||||
"Target", "Mode", "NativeProjectPath", "MIPSToolchainPath",
|
||||
"PCSXReduxPath", "PCSXReduxPCdrvBase", "SerialPort", "SerialBaudRate",
|
||||
"ResWidth", "ResHeight", "DualBuffering", "VerticalLayout",
|
||||
"GTEScaling", "AutoValidate", "InterceptPlayMode",
|
||||
"GTEScaling", "AutoValidate",
|
||||
"LicenseFilePath", "ISOVolumeLabel"
|
||||
};
|
||||
|
||||
|
||||
@@ -41,96 +41,62 @@ namespace SplashEdit.EditorCode
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
DrawHeader();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
_interactionFoldout = DrawFoldoutSection("Interaction Settings", _interactionFoldout, () =>
|
||||
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Label(EditorGUIUtility.IconContent("d_Selectable Icon"), GUILayout.Width(30), GUILayout.Height(30));
|
||||
EditorGUILayout.BeginVertical();
|
||||
EditorGUILayout.LabelField("PSX Interactable", PSXEditorStyles.CardHeaderStyle);
|
||||
EditorGUILayout.LabelField("Player interaction trigger for PS1", PSXEditorStyles.RichLabel);
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
_interactionFoldout = PSXEditorStyles.DrawFoldoutCard("Interaction Settings", _interactionFoldout, () =>
|
||||
{
|
||||
EditorGUILayout.PropertyField(_interactionRadius);
|
||||
|
||||
// Button selector with visual
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Interact Button");
|
||||
_interactButton.intValue = EditorGUILayout.Popup(_interactButton.intValue, ButtonNames);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
|
||||
EditorGUILayout.PropertyField(_isRepeatable);
|
||||
|
||||
|
||||
if (_isRepeatable.boolValue)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_cooldownFrames, new GUIContent("Cooldown (frames)"));
|
||||
|
||||
// Show cooldown in seconds
|
||||
|
||||
float seconds = _cooldownFrames.intValue / 60f;
|
||||
EditorGUILayout.LabelField($"≈ {seconds:F2} seconds at 60fps", EditorStyles.miniLabel);
|
||||
EditorGUILayout.LabelField($"~ {seconds:F2} seconds at 60fps", EditorStyles.miniLabel);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.PropertyField(_showPrompt);
|
||||
});
|
||||
|
||||
_advancedFoldout = DrawFoldoutSection("Advanced", _advancedFoldout, () =>
|
||||
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
_advancedFoldout = PSXEditorStyles.DrawFoldoutCard("Advanced", _advancedFoldout, () =>
|
||||
{
|
||||
EditorGUILayout.PropertyField(_requireLineOfSight);
|
||||
EditorGUILayout.PropertyField(_interactionOffset);
|
||||
});
|
||||
|
||||
DrawLuaEventsInfo(new[] { "onInteract" });
|
||||
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Lua events card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Lua Events", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
EditorGUILayout.LabelField("onInteract", PSXEditorStyles.RichLabel);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
|
||||
|
||||
GUILayout.Label(EditorGUIUtility.IconContent("d_Selectable Icon"), GUILayout.Width(30), GUILayout.Height(30));
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.Label("PSX Interactable", EditorStyles.boldLabel);
|
||||
GUILayout.Label("Player interaction trigger for PS1", EditorStyles.miniLabel);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private bool DrawFoldoutSection(string title, bool isExpanded, System.Action drawContent)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
isExpanded = EditorGUILayout.Foldout(isExpanded, title, true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (isExpanded)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
drawContent?.Invoke();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(3);
|
||||
|
||||
return isExpanded;
|
||||
}
|
||||
|
||||
private void DrawLuaEventsInfo(string[] events)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
GUILayout.Label("Lua Events", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
foreach (var evt in events)
|
||||
{
|
||||
GUILayout.Label($"• {evt}", EditorStyles.miniLabel);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
268
Editor/Inspectors/PSXComponentEditors2.cs
Normal file
268
Editor/Inspectors/PSXComponentEditors2.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using SplashEdit.RuntimeCode;
|
||||
|
||||
namespace SplashEdit.EditorCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom inspector for PSXAudioClip component.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(PSXAudioClip))]
|
||||
public class PSXAudioClipEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX Audio Clip", PSXEditorStyles.CardHeaderStyle);
|
||||
|
||||
PSXAudioClip audioClip = (PSXAudioClip)target;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (audioClip.Clip != null)
|
||||
PSXEditorStyles.DrawStatusBadge("Clip Set", PSXEditorStyles.Success, 70);
|
||||
else
|
||||
PSXEditorStyles.DrawStatusBadge("No Clip", PSXEditorStyles.Warning, 70);
|
||||
|
||||
if (audioClip.Loop)
|
||||
PSXEditorStyles.DrawStatusBadge("Loop", PSXEditorStyles.AccentCyan, 50);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Clip Settings", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("ClipName"), new GUIContent("Clip Name",
|
||||
"Name used to identify this clip in Lua (Audio.Play(\"name\"))."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("Clip"), new GUIContent("Audio Clip",
|
||||
"Unity AudioClip to convert to PS1 SPU ADPCM format."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("SampleRate"), new GUIContent("Sample Rate",
|
||||
"Target sample rate for the PS1 (lower = smaller, max 44100)."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("Loop"), new GUIContent("Loop",
|
||||
"Whether this clip should loop when played."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("DefaultVolume"), new GUIContent("Volume",
|
||||
"Default playback volume (0-127)."));
|
||||
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Info card
|
||||
if (audioClip.Clip != null)
|
||||
{
|
||||
PSXEditorStyles.BeginCard();
|
||||
float duration = audioClip.Clip.length;
|
||||
int srcRate = audioClip.Clip.frequency;
|
||||
EditorGUILayout.LabelField(
|
||||
$"Source: {srcRate} Hz, {duration:F2}s, {audioClip.Clip.channels}ch\n" +
|
||||
$"Target: {audioClip.SampleRate} Hz SPU ADPCM",
|
||||
PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom inspector for PSXPlayer component.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(PSXPlayer))]
|
||||
public class PSXPlayerEditor : Editor
|
||||
{
|
||||
private bool _dimensionsFoldout = true;
|
||||
private bool _movementFoldout = true;
|
||||
private bool _navigationFoldout = true;
|
||||
private bool _physicsFoldout = true;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX Player", PSXEditorStyles.CardHeaderStyle);
|
||||
EditorGUILayout.LabelField("First-person player controller for PS1", PSXEditorStyles.RichLabel);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Dimensions
|
||||
_dimensionsFoldout = PSXEditorStyles.DrawFoldoutCard("Player Dimensions", _dimensionsFoldout, () =>
|
||||
{
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("playerHeight"), new GUIContent("Height",
|
||||
"Camera eye height above the player's feet."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("playerRadius"), new GUIContent("Radius",
|
||||
"Collision radius for wall sliding."));
|
||||
});
|
||||
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
// Movement
|
||||
_movementFoldout = PSXEditorStyles.DrawFoldoutCard("Movement", _movementFoldout, () =>
|
||||
{
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("moveSpeed"), new GUIContent("Walk Speed",
|
||||
"Walk speed in world units per second."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("sprintSpeed"), new GUIContent("Sprint Speed",
|
||||
"Sprint speed in world units per second."));
|
||||
});
|
||||
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
// Navigation
|
||||
_navigationFoldout = PSXEditorStyles.DrawFoldoutCard("Navigation", _navigationFoldout, () =>
|
||||
{
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("maxStepHeight"), new GUIContent("Max Step Height",
|
||||
"Maximum height the agent can step up."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("walkableSlopeAngle"), new GUIContent("Walkable Slope",
|
||||
"Maximum walkable slope angle in degrees."));
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("navCellSize"), new GUIContent("Cell Size (XZ)",
|
||||
"Voxel size in XZ plane (smaller = more accurate but slower)."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("navCellHeight"), new GUIContent("Cell Height",
|
||||
"Voxel height (smaller = more accurate vertical resolution)."));
|
||||
});
|
||||
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
// Jump & Gravity
|
||||
_physicsFoldout = PSXEditorStyles.DrawFoldoutCard("Jump & Gravity", _physicsFoldout, () =>
|
||||
{
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("jumpHeight"), new GUIContent("Jump Height",
|
||||
"Peak jump height in world units."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("gravity"), new GUIContent("Gravity",
|
||||
"Downward acceleration in world units per second squared."));
|
||||
});
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom inspector for PSXPortalLink component.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(PSXPortalLink))]
|
||||
public class PSXPortalLinkEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
PSXPortalLink portal = (PSXPortalLink)target;
|
||||
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX Portal Link", PSXEditorStyles.CardHeaderStyle);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
bool valid = portal.RoomA != null && portal.RoomB != null && portal.RoomA != portal.RoomB;
|
||||
if (valid)
|
||||
PSXEditorStyles.DrawStatusBadge("Valid", PSXEditorStyles.Success, 55);
|
||||
else
|
||||
PSXEditorStyles.DrawStatusBadge("Invalid", PSXEditorStyles.Error, 60);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Room references card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Connected Rooms", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("RoomA"), new GUIContent("Room A",
|
||||
"First room connected by this portal."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("RoomB"), new GUIContent("Room B",
|
||||
"Second room connected by this portal."));
|
||||
|
||||
// Validation warnings
|
||||
if (portal.RoomA == null || portal.RoomB == null)
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.LabelField("Both Room A and Room B must be assigned for export.", PSXEditorStyles.InfoBox);
|
||||
}
|
||||
else if (portal.RoomA == portal.RoomB)
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.LabelField("Room A and Room B must be different rooms.", PSXEditorStyles.InfoBox);
|
||||
}
|
||||
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Portal size card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Portal Dimensions", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("PortalSize"), new GUIContent("Size (W, H)",
|
||||
"Size of the portal opening (width, height) in world units."));
|
||||
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom inspector for PSXRoom component.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(PSXRoom))]
|
||||
public class PSXRoomEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
PSXRoom room = (PSXRoom)target;
|
||||
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX Room", PSXEditorStyles.CardHeaderStyle);
|
||||
if (!string.IsNullOrEmpty(room.RoomName))
|
||||
EditorGUILayout.LabelField(room.RoomName, PSXEditorStyles.RichLabel);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Room Settings", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("RoomName"), new GUIContent("Room Name",
|
||||
"Optional display name for this room (used in editor gizmos)."));
|
||||
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("VolumeSize"), new GUIContent("Volume Size",
|
||||
"Size of the room volume in local space."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("VolumeOffset"), new GUIContent("Volume Offset",
|
||||
"Offset of the volume center relative to the transform position."));
|
||||
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Info card
|
||||
PSXEditorStyles.BeginCard();
|
||||
Bounds wb = room.GetWorldBounds();
|
||||
Vector3 size = wb.size;
|
||||
EditorGUILayout.LabelField(
|
||||
$"World bounds: {size.x:F1} x {size.y:F1} x {size.z:F1}",
|
||||
PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Editor/Inspectors/PSXComponentEditors2.cs.meta
Normal file
2
Editor/Inspectors/PSXComponentEditors2.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fd7a7bcc7d0ff841b158f2744d48010
|
||||
@@ -232,9 +232,16 @@ namespace SplashEdit.EditorCode
|
||||
serializedObject.Update();
|
||||
|
||||
Vector2 res = PSXCanvas.PSXResolution;
|
||||
EditorGUILayout.LabelField($"PSX Canvas ({res.x}x{res.y})", EditorStyles.boldLabel);
|
||||
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField($"PSX Canvas ({res.x}x{res.y})", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("canvasName"), new GUIContent("Canvas Name",
|
||||
"Name used from Lua: UI.FindCanvas(\"name\"). Max 24 chars."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("startVisible"), new GUIContent("Start Visible",
|
||||
@@ -244,18 +251,18 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("defaultFont"), new GUIContent("Default Font",
|
||||
"Default custom font for text elements. If empty, uses built-in system font (8x16)."));
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
PSXEditorStyles.DrawSeparator(6, 6);
|
||||
|
||||
// Force Canvas configuration button
|
||||
if (GUILayout.Button($"Reset Canvas to {res.x}x{res.y}"))
|
||||
if (GUILayout.Button($"Reset Canvas to {res.x}x{res.y}", PSXEditorStyles.SecondaryButton))
|
||||
{
|
||||
PSXCanvas.InvalidateResolutionCache();
|
||||
((PSXCanvas)target).ConfigureCanvas();
|
||||
}
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Element summary
|
||||
// Element summary card
|
||||
PSXCanvas canvas = (PSXCanvas)target;
|
||||
int imageCount = canvas.GetComponentsInChildren<PSXUIImage>(true).Length;
|
||||
int boxCount = canvas.GetComponentsInChildren<PSXUIBox>(true).Length;
|
||||
@@ -263,14 +270,16 @@ namespace SplashEdit.EditorCode
|
||||
int progressCount = canvas.GetComponentsInChildren<PSXUIProgressBar>(true).Length;
|
||||
int total = imageCount + boxCount + textCount + progressCount;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField(
|
||||
$"Elements: {total} total\n" +
|
||||
$" Images: {imageCount} | Boxes: {boxCount}\n" +
|
||||
$" Texts: {textCount} | Progress Bars: {progressCount}",
|
||||
total > 128 ? MessageType.Warning : MessageType.Info);
|
||||
PSXEditorStyles.InfoBox);
|
||||
|
||||
if (total > 128)
|
||||
EditorGUILayout.HelpBox("PS1 UI system supports max 128 elements total across all canvases.", MessageType.Error);
|
||||
EditorGUILayout.LabelField("PS1 UI system supports max 128 elements total across all canvases.", PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
@@ -286,9 +295,15 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("PSX UI Image", EditorStyles.boldLabel);
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX UI Image", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("elementName"), new GUIContent("Element Name",
|
||||
"Name used from Lua: UI.FindElement(canvas, \"name\"). Max 24 chars."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("sourceTexture"), new GUIContent("Source Texture",
|
||||
@@ -298,13 +313,19 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("tintColor"), new GUIContent("Tint Color",
|
||||
"Color multiply applied to the image (white = no tint)."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("startVisible"));
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
// Texture size warning
|
||||
PSXUIImage img = (PSXUIImage)target;
|
||||
if (img.SourceTexture != null)
|
||||
{
|
||||
if (img.SourceTexture.width > 256 || img.SourceTexture.height > 256)
|
||||
EditorGUILayout.HelpBox("Texture exceeds 256x256. It will be resized during export.", MessageType.Warning);
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Texture exceeds 256x256. It will be resized during export.", PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
@@ -321,13 +342,20 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("PSX UI Box", EditorStyles.boldLabel);
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX UI Box", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("elementName"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("boxColor"), new GUIContent("Box Color",
|
||||
"Solid fill color rendered as a FastFill primitive."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("startVisible"));
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
@@ -343,9 +371,15 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("PSX UI Text", EditorStyles.boldLabel);
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX UI Text", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("elementName"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("defaultText"), new GUIContent("Default Text",
|
||||
"Initial text content. Max 63 chars. Change at runtime via UI.SetText()."));
|
||||
@@ -354,24 +388,33 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("fontOverride"), new GUIContent("Font Override",
|
||||
"Custom font for this text element. If empty, uses the canvas default font or built-in system font (8x16)."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("startVisible"));
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
// Character count
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Warnings and info
|
||||
PSXUIText txt = (PSXUIText)target;
|
||||
if (!string.IsNullOrEmpty(txt.DefaultText) && txt.DefaultText.Length > 63)
|
||||
EditorGUILayout.HelpBox("Text exceeds 63 characters and will be truncated.", MessageType.Warning);
|
||||
{
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Text exceeds 63 characters and will be truncated.", PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
EditorGUILayout.Space(4);
|
||||
}
|
||||
|
||||
// Font info
|
||||
PSXEditorStyles.BeginCard();
|
||||
PSXFontAsset font = txt.GetEffectiveFont();
|
||||
if (font != null)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
EditorGUILayout.LabelField(
|
||||
$"Font: {font.name} ({font.GlyphWidth}x{font.GlyphHeight} glyphs)",
|
||||
MessageType.Info);
|
||||
PSXEditorStyles.InfoBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Using built-in system font (8x16 glyphs).", MessageType.Info);
|
||||
EditorGUILayout.LabelField("Using built-in system font (8x16 glyphs).", PSXEditorStyles.InfoBox);
|
||||
}
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
@@ -387,9 +430,15 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("PSX UI Progress Bar", EditorStyles.boldLabel);
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX UI Progress Bar", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("elementName"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("backgroundColor"), new GUIContent("Background Color",
|
||||
"Color shown behind the fill bar."));
|
||||
@@ -399,13 +448,12 @@ namespace SplashEdit.EditorCode
|
||||
"Starting progress (0-100). Change via UI.SetProgress()."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("startVisible"));
|
||||
|
||||
PSXEditorStyles.DrawSeparator(6, 4);
|
||||
|
||||
// Preview bar
|
||||
Rect r = EditorGUILayout.GetControlRect(false, 16);
|
||||
PSXUIProgressBar bar = (PSXUIProgressBar)target;
|
||||
EditorGUI.DrawRect(r, bar.BackgroundColor);
|
||||
Rect fill = r;
|
||||
fill.width *= bar.InitialValue / 100f;
|
||||
EditorGUI.DrawRect(fill, bar.FillColor);
|
||||
PSXEditorStyles.DrawProgressBar(bar.InitialValue / 100f, "Preview", bar.FillColor, 16);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
@@ -424,11 +472,18 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
PSXFontAsset font = (PSXFontAsset)target;
|
||||
|
||||
EditorGUILayout.LabelField("PSX Font Asset", EditorStyles.boldLabel);
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX Font Asset", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── Source font (TTF/OTF) ──
|
||||
EditorGUILayout.LabelField("Auto-Convert from Font", EditorStyles.miniBoldLabel);
|
||||
// Source font card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Auto-Convert from Font", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("sourceFont"), new GUIContent("Source Font (TTF/OTF)",
|
||||
"Assign a Unity Font (TrueType/OpenType). Click 'Generate Bitmap' to rasterize it.\n" +
|
||||
"Glyph cell dimensions are auto-derived from the font metrics."));
|
||||
@@ -440,102 +495,121 @@ namespace SplashEdit.EditorCode
|
||||
if (font.SourceFont != null)
|
||||
{
|
||||
EditorGUILayout.Space(2);
|
||||
if (GUILayout.Button("Generate Bitmap from Font", GUILayout.Height(28)))
|
||||
if (GUILayout.Button("Generate Bitmap from Font", PSXEditorStyles.PrimaryButton, GUILayout.Height(28)))
|
||||
{
|
||||
Undo.RecordObject(font, "Generate PSX Font Bitmap");
|
||||
font.GenerateBitmapFromFont();
|
||||
}
|
||||
|
||||
if (font.FontTexture == null)
|
||||
EditorGUILayout.HelpBox(
|
||||
EditorGUILayout.LabelField(
|
||||
"Click 'Generate Bitmap' to create the font texture.\n" +
|
||||
"If generation fails, check that the font's import settings have " +
|
||||
"'Character' set to 'ASCII Default Set'.", MessageType.Info);
|
||||
"'Character' set to 'ASCII Default Set'.", PSXEditorStyles.InfoBox);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(8);
|
||||
|
||||
// ── Manual bitmap ──
|
||||
EditorGUILayout.LabelField("Manual Bitmap Source", EditorStyles.miniBoldLabel);
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("fontTexture"), new GUIContent("Font Texture",
|
||||
"256px wide bitmap. Glyphs in ASCII order from 0x20 (space). " +
|
||||
"Transparent = background, opaque = foreground."));
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── Glyph metrics ──
|
||||
// If using auto-convert, these are set by GenerateBitmapFromFont.
|
||||
// For manual bitmaps, the user sets them directly.
|
||||
EditorGUILayout.LabelField("Glyph Metrics", EditorStyles.miniBoldLabel);
|
||||
// Manual bitmap card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Manual Bitmap Source", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("fontTexture"), new GUIContent("Font Texture",
|
||||
"256px wide bitmap. Glyphs in ASCII order from 0x20 (space). " +
|
||||
"Transparent = background, opaque = foreground."));
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Glyph metrics card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Glyph Metrics", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
|
||||
if (font.SourceFont != null && font.FontTexture != null)
|
||||
{
|
||||
// Show as read-only when auto-generated
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.IntField(new GUIContent("Glyph Width", "Auto-derived from font. Re-generate to change."), font.GlyphWidth);
|
||||
EditorGUILayout.IntField(new GUIContent("Glyph Height", "Auto-derived from font. Re-generate to change."), font.GlyphHeight);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
EditorGUILayout.HelpBox("Glyph dimensions are auto-derived when generating from a font.\n" +
|
||||
"Change the Font Size slider and re-generate to adjust.", MessageType.Info);
|
||||
EditorGUILayout.LabelField("Glyph dimensions are auto-derived when generating from a font.\n" +
|
||||
"Change the Font Size slider and re-generate to adjust.", PSXEditorStyles.InfoBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Editable for manual bitmap workflow
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("glyphWidth"), new GUIContent("Glyph Width",
|
||||
"Width of each glyph cell in pixels. Must divide 256 evenly (4, 8, 16, or 32)."));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("glyphHeight"), new GUIContent("Glyph Height",
|
||||
"Height of each glyph cell in pixels."));
|
||||
}
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── Layout info ──
|
||||
// Layout info card
|
||||
PSXEditorStyles.BeginCard();
|
||||
int glyphsPerRow = font.GlyphsPerRow;
|
||||
int rowCount = font.RowCount;
|
||||
int totalH = font.TextureHeight;
|
||||
int vramBytes = totalH * 128; // 128 bytes per row at 4bpp 256px
|
||||
int vramBytes = totalH * 128;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
EditorGUILayout.LabelField(
|
||||
$"Layout: {glyphsPerRow} glyphs/row, {rowCount} rows\n" +
|
||||
$"Texture: 256 x {totalH} pixels (4bpp)\n" +
|
||||
$"VRAM: {vramBytes} bytes ({vramBytes / 1024f:F1} KB)\n" +
|
||||
$"Glyph cell: {font.GlyphWidth} x {font.GlyphHeight}",
|
||||
MessageType.Info);
|
||||
PSXEditorStyles.InfoBox);
|
||||
|
||||
// Show advance width status
|
||||
if (font.AdvanceWidths != null && font.AdvanceWidths.Length >= 95)
|
||||
{
|
||||
int minAdv = 255, maxAdv = 0;
|
||||
for (int i = 1; i < 95; i++) // skip space
|
||||
for (int i = 1; i < 95; i++)
|
||||
{
|
||||
if (font.AdvanceWidths[i] < minAdv) minAdv = font.AdvanceWidths[i];
|
||||
if (font.AdvanceWidths[i] > maxAdv) maxAdv = font.AdvanceWidths[i];
|
||||
}
|
||||
EditorGUILayout.HelpBox(
|
||||
EditorGUILayout.LabelField(
|
||||
$"Advance widths: {minAdv}-{maxAdv}px (proportional spacing stored)",
|
||||
MessageType.Info);
|
||||
PSXEditorStyles.InfoBox);
|
||||
}
|
||||
else if (font.FontTexture != null)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
EditorGUILayout.LabelField(
|
||||
"No advance widths stored. Click 'Generate Bitmap' to compute them.",
|
||||
MessageType.Warning);
|
||||
PSXEditorStyles.InfoBox);
|
||||
}
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
// ── Validation ──
|
||||
// Validation
|
||||
if (font.FontTexture != null)
|
||||
{
|
||||
if (font.FontTexture.width != 256)
|
||||
EditorGUILayout.HelpBox($"Font texture must be 256 pixels wide (currently {font.FontTexture.width}).", MessageType.Error);
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField($"Font texture must be 256 pixels wide (currently {font.FontTexture.width}).", PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
}
|
||||
|
||||
if (256 % font.GlyphWidth != 0)
|
||||
EditorGUILayout.HelpBox($"Glyph width ({font.GlyphWidth}) must divide 256 evenly. " +
|
||||
"Valid values: 4, 8, 16, 32.", MessageType.Error);
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField($"Glyph width ({font.GlyphWidth}) must divide 256 evenly. " +
|
||||
"Valid values: 4, 8, 16, 32.", PSXEditorStyles.InfoBox);
|
||||
PSXEditorStyles.EndCard();
|
||||
}
|
||||
|
||||
// Show preview
|
||||
// Preview
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.LabelField("Preview", EditorStyles.miniBoldLabel);
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("Preview", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.DrawSeparator(2, 4);
|
||||
Rect previewRect = EditorGUILayout.GetControlRect(false, 64);
|
||||
GUI.DrawTexture(previewRect, font.FontTexture, ScaleMode.ScaleToFit);
|
||||
PSXEditorStyles.EndCard();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace SplashEdit.RuntimeCode
|
||||
var exporters = Object.FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
|
||||
foreach (var e in exporters)
|
||||
exporterNames.Add(e.gameObject.name);
|
||||
var audioSources = Object.FindObjectsByType<PSXAudioSource>(FindObjectsSortMode.None);
|
||||
var audioSources = Object.FindObjectsByType<PSXAudioClip>(FindObjectsSortMode.None);
|
||||
foreach (var a in audioSources)
|
||||
if (!string.IsNullOrEmpty(a.ClipName))
|
||||
audioNames.Add(a.ClipName);
|
||||
@@ -351,7 +351,7 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
evt.ClipName = EditorGUILayout.TextField("Clip Name", evt.ClipName);
|
||||
if (!string.IsNullOrEmpty(evt.ClipName) && !audioNames.Contains(evt.ClipName))
|
||||
EditorGUILayout.HelpBox($"No PSXAudioSource with ClipName '{evt.ClipName}' in scene.", MessageType.Error);
|
||||
EditorGUILayout.HelpBox($"No PSXAudioClip with ClipName '{evt.ClipName}' in scene.", MessageType.Error);
|
||||
|
||||
evt.Volume = EditorGUILayout.IntSlider("Volume", evt.Volume, 0, 128);
|
||||
evt.Pan = EditorGUILayout.IntSlider("Pan", evt.Pan, 0, 127);
|
||||
@@ -505,7 +505,7 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
// Build audio clip lookup
|
||||
_audioClipCache.Clear();
|
||||
var audioSources = Object.FindObjectsByType<PSXAudioSource>(FindObjectsSortMode.None);
|
||||
var audioSources = Object.FindObjectsByType<PSXAudioClip>(FindObjectsSortMode.None);
|
||||
foreach (var a in audioSources)
|
||||
if (!string.IsNullOrEmpty(a.ClipName) && a.Clip != null)
|
||||
_audioClipCache[a.ClipName] = a.Clip;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
// ───── Main Entry Point ─────
|
||||
|
||||
[MenuItem(MENU_ROOT + "SplashEdit Control Panel %#p", false, 0)]
|
||||
[MenuItem(MENU_ROOT + "SplashEdit Control Panel %#l", false, 0)]
|
||||
public static void OpenControlPanel()
|
||||
{
|
||||
SplashControlPanel.ShowWindow();
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SplashEdit.EditorCode
|
||||
private int _selectedRegion = -1;
|
||||
private bool _showAdvanced = false;
|
||||
|
||||
[MenuItem("PSX/Nav Region Builder")]
|
||||
[MenuItem("PlayStation 1/Nav Region Builder")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<PSXNavRegionEditor>("Nav Region Builder");
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
if (meshFilter == null || meshFilter.sharedMesh == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No mesh on this object.", MessageType.Warning);
|
||||
EditorGUILayout.LabelField("No mesh on this object.", PSXEditorStyles.InfoBox);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,9 +125,9 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(EditorGUI.indentLevel * 15);
|
||||
if (GUILayout.Button("Edit", EditorStyles.miniButtonLeft, GUILayout.Width(50)))
|
||||
if (GUILayout.Button("Edit", PSXEditorStyles.SecondaryButton, GUILayout.Width(50)))
|
||||
AssetDatabase.OpenAsset(luaFileProp.objectReferenceValue);
|
||||
if (GUILayout.Button("Clear", EditorStyles.miniButtonRight, GUILayout.Width(50)))
|
||||
if (GUILayout.Button("Clear", PSXEditorStyles.SecondaryButton, GUILayout.Width(50)))
|
||||
luaFileProp.objectReferenceValue = null;
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
@@ -136,7 +136,7 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(EditorGUI.indentLevel * 15);
|
||||
if (GUILayout.Button("Create Lua Script", EditorStyles.miniButton, GUILayout.Width(130)))
|
||||
if (GUILayout.Button("Create Lua Script", PSXEditorStyles.SecondaryButton, GUILayout.Width(130)))
|
||||
CreateNewLuaScript();
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
@@ -174,7 +174,7 @@ namespace SplashEdit.EditorCode
|
||||
private void DrawActions()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Select Scene Exporter", EditorStyles.miniButton))
|
||||
if (GUILayout.Button("Select Scene Exporter", PSXEditorStyles.SecondaryButton))
|
||||
{
|
||||
var se = FindFirstObjectByType<PSXSceneExporter>();
|
||||
if (se != null)
|
||||
|
||||
@@ -17,15 +17,9 @@ namespace SplashEdit.EditorCode
|
||||
private SerializedProperty cutscenesProp;
|
||||
private SerializedProperty loadingScreenProp;
|
||||
private SerializedProperty previewBVHProp;
|
||||
private SerializedProperty previewRoomsPortalsProp;
|
||||
private SerializedProperty bvhDepthProp;
|
||||
|
||||
private bool _savedFog;
|
||||
private Color _savedFogColor;
|
||||
private FogMode _savedFogMode;
|
||||
private float _savedFogStart;
|
||||
private float _savedFogEnd;
|
||||
private bool _previewActive = false;
|
||||
|
||||
private bool showFog = true;
|
||||
private bool showCutscenes = true;
|
||||
private bool showDebug = false;
|
||||
@@ -41,22 +35,12 @@ namespace SplashEdit.EditorCode
|
||||
cutscenesProp = serializedObject.FindProperty("Cutscenes");
|
||||
loadingScreenProp = serializedObject.FindProperty("LoadingScreenPrefab");
|
||||
previewBVHProp = serializedObject.FindProperty("PreviewBVH");
|
||||
previewRoomsPortalsProp = serializedObject.FindProperty("PreviewRoomsPortals");
|
||||
bvhDepthProp = serializedObject.FindProperty("BVHPreviewDepth");
|
||||
|
||||
SaveAndApplyFogPreview();
|
||||
EditorApplication.update += OnEditorUpdate;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorApplication.update -= OnEditorUpdate;
|
||||
RestoreFog();
|
||||
}
|
||||
|
||||
private void OnEditorUpdate()
|
||||
{
|
||||
if (_previewActive)
|
||||
ApplyFogPreview();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
@@ -64,7 +48,7 @@ namespace SplashEdit.EditorCode
|
||||
serializedObject.Update();
|
||||
var exporter = (PSXSceneExporter)target;
|
||||
|
||||
DrawHeader();
|
||||
DrawExporterHeader();
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
DrawSceneSettings();
|
||||
@@ -82,7 +66,7 @@ namespace SplashEdit.EditorCode
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
private void DrawExporterHeader()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
|
||||
EditorGUILayout.LabelField("Scene Exporter", PSXEditorStyles.CardHeaderStyle);
|
||||
@@ -138,15 +122,9 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
EditorGUILayout.Space(2);
|
||||
EditorGUILayout.LabelField(
|
||||
$"<color=#aaaaaa>Preview: {fogNearUnity:F1} – {fogFarUnity:F1} units | " +
|
||||
$"GTE: {8000f / (density * 3f):F0} – {8000f / density:F0} SZ</color>",
|
||||
$"<color=#aaaaaa>GTE range: {fogNearUnity:F1} - {fogFarUnity:F1} units | " +
|
||||
$"{8000f / (density * 3f):F0} - {8000f / density:F0} SZ</color>",
|
||||
PSXEditorStyles.RichLabel);
|
||||
|
||||
ApplyFogPreview();
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderSettings.fog = false;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
@@ -186,17 +164,18 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.PropertyField(previewBVHProp, new GUIContent("Preview BVH"));
|
||||
if (previewBVHProp.boolValue)
|
||||
EditorGUILayout.PropertyField(bvhDepthProp, new GUIContent("BVH Depth"));
|
||||
EditorGUILayout.PropertyField(previewRoomsPortalsProp, new GUIContent("Preview Rooms/Portals"));
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
private void DrawSceneStats()
|
||||
{
|
||||
var exporters = FindObjectsOfType<PSXObjectExporter>();
|
||||
var exporters = FindObjectsByType<PSXObjectExporter>(FindObjectsSortMode.None);
|
||||
int total = exporters.Length;
|
||||
int active = exporters.Count(e => e.IsActive);
|
||||
int staticCol = exporters.Count(e => e.CollisionType == PSXCollisionType.Static);
|
||||
int dynamicCol = exporters.Count(e => e.CollisionType == PSXCollisionType.Dynamic);
|
||||
int triggerBoxes = FindObjectsOfType<PSXTriggerBox>().Length;
|
||||
int triggerBoxes = FindObjectsByType<PSXTriggerBox>(FindObjectsSortMode.None).Length;
|
||||
|
||||
EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle);
|
||||
EditorGUILayout.LabelField(
|
||||
@@ -205,49 +184,5 @@ namespace SplashEdit.EditorCode
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void SaveAndApplyFogPreview()
|
||||
{
|
||||
_savedFog = RenderSettings.fog;
|
||||
_savedFogColor = RenderSettings.fogColor;
|
||||
_savedFogMode = RenderSettings.fogMode;
|
||||
_savedFogStart = RenderSettings.fogStartDistance;
|
||||
_savedFogEnd = RenderSettings.fogEndDistance;
|
||||
_previewActive = true;
|
||||
ApplyFogPreview();
|
||||
}
|
||||
|
||||
private void ApplyFogPreview()
|
||||
{
|
||||
var exporter = (PSXSceneExporter)target;
|
||||
if (exporter == null) return;
|
||||
|
||||
if (!exporter.FogEnabled)
|
||||
{
|
||||
RenderSettings.fog = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float gteScale = exporter.GTEScaling;
|
||||
int density = Mathf.Clamp(exporter.FogDensity, 1, 10);
|
||||
float fogFarSZ = 8000f / density;
|
||||
float fogNearSZ = fogFarSZ / 3f;
|
||||
|
||||
RenderSettings.fog = true;
|
||||
RenderSettings.fogColor = exporter.FogColor;
|
||||
RenderSettings.fogMode = FogMode.Linear;
|
||||
RenderSettings.fogStartDistance = fogNearSZ * gteScale / 4096f;
|
||||
RenderSettings.fogEndDistance = fogFarSZ * gteScale / 4096f;
|
||||
}
|
||||
|
||||
private void RestoreFog()
|
||||
{
|
||||
if (!_previewActive) return;
|
||||
_previewActive = false;
|
||||
RenderSettings.fog = _savedFog;
|
||||
RenderSettings.fogColor = _savedFogColor;
|
||||
RenderSettings.fogMode = _savedFogMode;
|
||||
RenderSettings.fogStartDistance = _savedFogStart;
|
||||
RenderSettings.fogEndDistance = _savedFogEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,35 @@ namespace SplashEdit.EditorCode
|
||||
private void OnEnable()
|
||||
{
|
||||
sizeProp = serializedObject.FindProperty("size");
|
||||
luaFileProp = serializedObject.FindProperty("luaFile");
|
||||
luaFileProp = serializedObject.FindProperty("luaFile");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("PSX Trigger Box", EditorStyles.boldLabel);
|
||||
// Header card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.LabelField("PSX Trigger Box", PSXEditorStyles.CardHeaderStyle);
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Properties card
|
||||
PSXEditorStyles.BeginCard();
|
||||
EditorGUILayout.PropertyField(sizeProp, new GUIContent("Size"));
|
||||
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
|
||||
EditorGUILayout.PropertyField(luaFileProp, new GUIContent("Lua Script"));
|
||||
|
||||
if (luaFileProp.objectReferenceValue != null)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(EditorGUI.indentLevel * 15);
|
||||
if (GUILayout.Button("Edit", EditorStyles.miniButtonLeft, GUILayout.Width(50)))
|
||||
if (GUILayout.Button("Edit", PSXEditorStyles.SecondaryButton, GUILayout.Width(50)))
|
||||
AssetDatabase.OpenAsset(luaFileProp.objectReferenceValue);
|
||||
if (GUILayout.Button("Clear", EditorStyles.miniButtonRight, GUILayout.Width(50)))
|
||||
if (GUILayout.Button("Clear", PSXEditorStyles.SecondaryButton, GUILayout.Width(50)))
|
||||
luaFileProp.objectReferenceValue = null;
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
@@ -41,11 +50,12 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(EditorGUI.indentLevel * 15);
|
||||
if (GUILayout.Button("Create Lua Script", EditorStyles.miniButton, GUILayout.Width(130)))
|
||||
if (GUILayout.Button("Create Lua Script", PSXEditorStyles.SecondaryButton, GUILayout.Width(130)))
|
||||
CreateNewLuaScript();
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
@@ -27,19 +27,25 @@ namespace SplashEdit.EditorCode
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Quantized Preview", EditorStyles.boldLabel);
|
||||
GUILayout.Label("Quantized Preview", PSXEditorStyles.WindowHeader);
|
||||
|
||||
// Texture input field
|
||||
PSXEditorStyles.BeginCard();
|
||||
originalTexture = (Texture2D)EditorGUILayout.ObjectField("Original Texture", originalTexture, typeof(Texture2D), false);
|
||||
|
||||
// Dropdown for bit depth selection
|
||||
bpp = (PSXBPP)EditorGUILayout.EnumPopup("Bit Depth", bpp);
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Button to generate the quantized preview
|
||||
if (GUILayout.Button("Generate Quantized Preview") && originalTexture != null)
|
||||
if (GUILayout.Button("Generate Quantized Preview", PSXEditorStyles.PrimaryButton, GUILayout.Height(26)) && originalTexture != null)
|
||||
{
|
||||
GenerateQuantizedPreview();
|
||||
}
|
||||
PSXEditorStyles.EndCard();
|
||||
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
@@ -47,7 +53,7 @@ namespace SplashEdit.EditorCode
|
||||
if (originalTexture != null)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Label("Original Texture");
|
||||
GUILayout.Label("Original Texture", PSXEditorStyles.CardHeaderStyle);
|
||||
DrawTexturePreview(originalTexture, previewSize, false);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
@@ -56,7 +62,7 @@ namespace SplashEdit.EditorCode
|
||||
if (vramTexture != null)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Label("VRAM View (Indexed Data as 16bpp)");
|
||||
GUILayout.Label("VRAM View (Indexed Data as 16bpp)", PSXEditorStyles.CardHeaderStyle);
|
||||
DrawTexturePreview(vramTexture, previewSize);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
@@ -65,7 +71,7 @@ namespace SplashEdit.EditorCode
|
||||
if (quantizedTexture != null)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Label("Quantized Texture");
|
||||
GUILayout.Label("Quantized Texture", PSXEditorStyles.CardHeaderStyle);
|
||||
DrawTexturePreview(quantizedTexture, previewSize);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
@@ -75,16 +81,17 @@ namespace SplashEdit.EditorCode
|
||||
// Display the Color Lookup Table (CLUT)
|
||||
if (clut != null)
|
||||
{
|
||||
GUILayout.Label("Color Lookup Table (CLUT)");
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
GUILayout.Label("Color Lookup Table (CLUT)", PSXEditorStyles.SectionHeader);
|
||||
DrawCLUT();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
|
||||
// Export indexed pixel data
|
||||
if (indexedPixelData != null)
|
||||
{
|
||||
if (GUILayout.Button("Export texture data"))
|
||||
if (GUILayout.Button("Export texture data", PSXEditorStyles.SecondaryButton, GUILayout.Height(24)))
|
||||
{
|
||||
string path = EditorUtility.SaveFilePanel("Save texture data", "", "pixel_data", "bin");
|
||||
|
||||
@@ -105,7 +112,7 @@ namespace SplashEdit.EditorCode
|
||||
// Export CLUT data
|
||||
if (clut != null)
|
||||
{
|
||||
if (GUILayout.Button("Export CLUT data"))
|
||||
if (GUILayout.Button("Export CLUT data", PSXEditorStyles.SecondaryButton, GUILayout.Height(24)))
|
||||
{
|
||||
string path = EditorUtility.SaveFilePanel("Save CLUT data", "", "clut_data", "bin");
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SplashEdit.RuntimeCode;
|
||||
using Unity.Collections;
|
||||
@@ -19,25 +18,15 @@ namespace SplashEdit.EditorCode
|
||||
private List<ProhibitedArea> prohibitedAreas = new List<ProhibitedArea>();
|
||||
private Vector2 scrollPosition;
|
||||
private Texture2D vramImage;
|
||||
private Vector2 selectedResolution = new Vector2(320, 240);
|
||||
private bool dualBuffering = true;
|
||||
private bool verticalLayout = true;
|
||||
private static readonly Vector2 selectedResolution = new Vector2(320, 240);
|
||||
private const bool dualBuffering = true;
|
||||
private const bool verticalLayout = true;
|
||||
private Color bufferColor1 = new Color(1, 0, 0, 0.5f);
|
||||
private Color bufferColor2 = new Color(0, 1, 0, 0.5f);
|
||||
private Color prohibitedColor = new Color(1, 0, 0, 0.3f);
|
||||
private PSXData _psxData;
|
||||
private PSXFontData[] _cachedFonts;
|
||||
|
||||
private static readonly Vector2[] resolutions =
|
||||
{
|
||||
new Vector2(256, 240), new Vector2(256, 480),
|
||||
new Vector2(320, 240), new Vector2(320, 480),
|
||||
new Vector2(368, 240), new Vector2(368, 480),
|
||||
new Vector2(512, 240), new Vector2(512, 480),
|
||||
new Vector2(640, 240), new Vector2(640, 480)
|
||||
};
|
||||
private static string[] resolutionsStrings => resolutions.Select(c => $"{c.x}x{c.y}").ToArray();
|
||||
|
||||
[MenuItem("PlayStation 1/VRAM Editor")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
@@ -58,7 +47,9 @@ namespace SplashEdit.EditorCode
|
||||
// Ensure minimum window size is applied.
|
||||
this.minSize = MinSize;
|
||||
|
||||
_psxData = DataStorage.LoadData(out selectedResolution, out dualBuffering, out verticalLayout, out prohibitedAreas);
|
||||
Vector2 ignoredRes;
|
||||
bool ignoredDb, ignoredVl;
|
||||
_psxData = DataStorage.LoadData(out ignoredRes, out ignoredDb, out ignoredVl, out prohibitedAreas);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,64 +193,18 @@ namespace SplashEdit.EditorCode
|
||||
}
|
||||
|
||||
vramImage.Apply();
|
||||
|
||||
// Prompt the user to select a file location and save the VRAM data.
|
||||
string path = EditorUtility.SaveFilePanel("Select Output File", "", "output", "bin");
|
||||
|
||||
if (path != string.Empty)
|
||||
{
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
|
||||
{
|
||||
for (int y = 0; y < VramHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < VramWidth; x++)
|
||||
{
|
||||
writer.Write(packed.vramPixels[x, y].Pack());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Label("VRAM Editor", EditorStyles.boldLabel);
|
||||
GUILayout.Label("VRAM Editor", PSXEditorStyles.WindowHeader);
|
||||
GUILayout.Label("320x240, dual-buffered, vertical layout", PSXEditorStyles.InfoBox);
|
||||
|
||||
// Dropdown for resolution selection.
|
||||
selectedResolution = resolutions[EditorGUILayout.Popup("Resolution", System.Array.IndexOf(resolutions, selectedResolution), resolutionsStrings)];
|
||||
|
||||
// Check resolution constraints for dual buffering.
|
||||
bool canDBHorizontal = selectedResolution.x * 2 <= VramWidth;
|
||||
bool canDBVertical = selectedResolution.y * 2 <= VramHeight;
|
||||
|
||||
if (canDBHorizontal || canDBVertical)
|
||||
{
|
||||
dualBuffering = EditorGUILayout.Toggle("Dual Buffering", dualBuffering);
|
||||
}
|
||||
else
|
||||
{
|
||||
dualBuffering = false;
|
||||
}
|
||||
|
||||
if (canDBVertical && canDBHorizontal)
|
||||
{
|
||||
verticalLayout = EditorGUILayout.Toggle("Vertical", verticalLayout);
|
||||
}
|
||||
else if (canDBVertical)
|
||||
{
|
||||
verticalLayout = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
verticalLayout = false;
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("Prohibited Areas", EditorStyles.boldLabel);
|
||||
GUILayout.Space(10);
|
||||
PSXEditorStyles.DrawSeparator(6, 6);
|
||||
GUILayout.Label("Prohibited Areas", PSXEditorStyles.SectionHeader);
|
||||
GUILayout.Space(4);
|
||||
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true, GUILayout.MinHeight(300f), GUILayout.ExpandWidth(true));
|
||||
|
||||
@@ -270,10 +215,7 @@ namespace SplashEdit.EditorCode
|
||||
{
|
||||
var area = prohibitedAreas[i];
|
||||
|
||||
GUI.backgroundColor = new Color(0.95f, 0.95f, 0.95f);
|
||||
GUILayout.BeginVertical("box");
|
||||
|
||||
GUI.backgroundColor = Color.white;
|
||||
PSXEditorStyles.BeginCard();
|
||||
|
||||
// Display fields for editing the area
|
||||
area.X = EditorGUILayout.IntField("X Coordinate", area.X);
|
||||
@@ -281,17 +223,16 @@ namespace SplashEdit.EditorCode
|
||||
area.Width = EditorGUILayout.IntField("Width", area.Width);
|
||||
area.Height = EditorGUILayout.IntField("Height", area.Height);
|
||||
|
||||
|
||||
if (GUILayout.Button("Remove", GUILayout.Height(30)))
|
||||
EditorGUILayout.Space(2);
|
||||
if (GUILayout.Button("Remove", PSXEditorStyles.DangerButton, GUILayout.Height(24)))
|
||||
{
|
||||
toRemove.Add(i); // Mark for removal
|
||||
}
|
||||
|
||||
|
||||
prohibitedAreas[i] = area;
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
PSXEditorStyles.EndCard();
|
||||
GUILayout.Space(4);
|
||||
}
|
||||
|
||||
// Remove the areas marked for deletion outside the loop to avoid skipping elements
|
||||
@@ -303,19 +244,23 @@ namespace SplashEdit.EditorCode
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.Space(10);
|
||||
|
||||
if (GUILayout.Button("Add Prohibited Area"))
|
||||
if (GUILayout.Button("Add Prohibited Area", PSXEditorStyles.SecondaryButton))
|
||||
{
|
||||
prohibitedAreas.Add(new ProhibitedArea());
|
||||
}
|
||||
|
||||
// Button to initiate texture packing.
|
||||
if (GUILayout.Button("Pack Textures"))
|
||||
PSXEditorStyles.DrawSeparator(4, 4);
|
||||
|
||||
// Button to pack and preview VRAM layout.
|
||||
if (GUILayout.Button("Pack Preview", PSXEditorStyles.PrimaryButton, GUILayout.Height(28)))
|
||||
{
|
||||
PackTextures();
|
||||
}
|
||||
|
||||
// Button to save settings; saving now occurs only on button press.
|
||||
if (GUILayout.Button("Save Settings"))
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
// Button to save prohibited areas.
|
||||
if (GUILayout.Button("Save Settings", PSXEditorStyles.SuccessButton, GUILayout.Height(28)))
|
||||
{
|
||||
_psxData.OutputResolution = selectedResolution;
|
||||
_psxData.DualBuffering = dualBuffering;
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace SplashEdit.RuntimeCode
|
||||
/// At export time, the AudioClip is converted to SPU ADPCM and packed
|
||||
/// into the splashpack binary. Use Audio.Play(clipIndex) from Lua.
|
||||
/// </summary>
|
||||
[AddComponentMenu("PSX/Audio Source")]
|
||||
public class PSXAudioSource : MonoBehaviour
|
||||
[AddComponentMenu("PSX/Audio Clip")]
|
||||
public class PSXAudioClip : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Name used to identify this clip in Lua (Audio.Play(\"name\"))." )]
|
||||
public string ClipName = "";
|
||||
2
Runtime/PSXAudioClip.cs.meta
Normal file
2
Runtime/PSXAudioClip.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0da2803235be654438e86fe9d9a954d4
|
||||
@@ -13,7 +13,7 @@ namespace SplashEdit.RuntimeCode
|
||||
[Tooltip("Frame at which to trigger this audio clip.")]
|
||||
public int Frame;
|
||||
|
||||
[Tooltip("Name of the audio clip (must match a PSXAudioSource ClipName in the scene).")]
|
||||
[Tooltip("Name of the audio clip (must match a PSXAudioClip ClipName in the scene).")]
|
||||
public string ClipName = "";
|
||||
|
||||
[Tooltip("Playback volume (0 = silent, 128 = max).")]
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c4c3feb30e8c264baddc3a5e774473b
|
||||
@@ -45,7 +45,7 @@ namespace SplashEdit.RuntimeCode
|
||||
BinaryWriter writer,
|
||||
PSXCutsceneClip[] cutscenes,
|
||||
PSXObjectExporter[] exporters,
|
||||
PSXAudioSource[] audioSources,
|
||||
PSXAudioClip[] audioSources,
|
||||
float gteScaling,
|
||||
out long cutsceneTableStart,
|
||||
Action<string, LogType> log = null)
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace SplashEdit.RuntimeCode
|
||||
Dynamic = 2
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(Renderer))]
|
||||
[RequireComponent(typeof(MeshFilter))]
|
||||
[RequireComponent(typeof(MeshRenderer))]
|
||||
public class PSXObjectExporter : MonoBehaviour, IPSXExportable
|
||||
{
|
||||
public LuaFile LuaFile => luaFile;
|
||||
|
||||
@@ -26,8 +26,11 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
var exporter = FindFirstObjectByType<PSXSceneExporter>();
|
||||
if (exporter != null && !exporter.PreviewRoomsPortals) return;
|
||||
|
||||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.3f);
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawCube(Vector3.zero, new Vector3(PortalSize.x, PortalSize.y, 0.05f));
|
||||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.8f);
|
||||
Gizmos.DrawWireCube(Vector3.zero, new Vector3(PortalSize.x, PortalSize.y, 0.05f));
|
||||
|
||||
@@ -56,6 +56,9 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
var exporter = FindFirstObjectByType<PSXSceneExporter>();
|
||||
if (exporter != null && !exporter.PreviewRoomsPortals) return;
|
||||
|
||||
Gizmos.color = new Color(0.2f, 0.8f, 0.4f, 0.15f);
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawCube(VolumeOffset, VolumeSize);
|
||||
@@ -66,7 +69,7 @@ namespace SplashEdit.RuntimeCode
|
||||
#if UNITY_EDITOR
|
||||
if (!string.IsNullOrEmpty(RoomName))
|
||||
{
|
||||
UnityEditor.Handles.Label(transform.TransformPoint(VolumeOffset),
|
||||
UnityEditor.Handles.Label(transform.TransformPoint(VolumeOffset),
|
||||
RoomName, new GUIStyle { normal = { textColor = Color.green } });
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
// Component arrays
|
||||
private PSXInteractable[] _interactables;
|
||||
private PSXAudioSource[] _audioSources;
|
||||
private PSXAudioClip[] _audioSources;
|
||||
private PSXTriggerBox[] _triggerBoxes;
|
||||
|
||||
// Phase 3+4: World collision and nav regions
|
||||
@@ -90,6 +90,8 @@ namespace SplashEdit.RuntimeCode
|
||||
private BVH _bvh;
|
||||
|
||||
public bool PreviewBVH = true;
|
||||
public bool PreviewRoomsPortals = true;
|
||||
|
||||
public int BVHPreviewDepth = 9999;
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +122,7 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
// Collect components
|
||||
_interactables = FindObjectsByType<PSXInteractable>(FindObjectsSortMode.None);
|
||||
_audioSources = FindObjectsByType<PSXAudioSource>(FindObjectsSortMode.None);
|
||||
_audioSources = FindObjectsByType<PSXAudioClip>(FindObjectsSortMode.None);
|
||||
_triggerBoxes = FindObjectsByType<PSXTriggerBox>(FindObjectsSortMode.None);
|
||||
|
||||
// Collect UI image textures for VRAM packing alongside 3D textures
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
// Cutscene data (v12)
|
||||
public PSXCutsceneClip[] cutscenes;
|
||||
public PSXAudioSource[] audioSources;
|
||||
public PSXAudioClip[] audioSources;
|
||||
|
||||
// UI canvases (v13)
|
||||
public PSXCanvasData[] canvases;
|
||||
|
||||
@@ -154,6 +154,18 @@ namespace SplashEdit.RuntimeCode
|
||||
G = (ushort)(pixel.g * 31),
|
||||
B = (ushort)(pixel.b * 31)
|
||||
};
|
||||
|
||||
// PS1: color 0x0000 is transparent. If the source pixel is opaque
|
||||
// but quantized to pure black, bump to near-black (1,1,1) with bit15
|
||||
// set so the hardware doesn't treat it as see-through.
|
||||
if (vramPixel.Pack() == 0x0000 && pixel.a > 0f)
|
||||
{
|
||||
vramPixel.R = 1;
|
||||
vramPixel.G = 1;
|
||||
vramPixel.B = 1;
|
||||
vramPixel.SemiTransparent = true;
|
||||
}
|
||||
|
||||
psxTex.ImageData[x, y] = vramPixel;
|
||||
}
|
||||
}
|
||||
@@ -169,6 +181,18 @@ namespace SplashEdit.RuntimeCode
|
||||
{
|
||||
Color pixel = new Color(color.x, color.y, color.z);
|
||||
VRAMPixel vramPixel = new VRAMPixel { R = (ushort)(pixel.r * 31), G = (ushort)(pixel.g * 31), B = (ushort)(pixel.b * 31) };
|
||||
|
||||
// PS1: palette entry 0x0000 is transparent. Any non-transparent palette
|
||||
// color that quantizes to pure black must be bumped to near-black (1,1,1)
|
||||
// with bit15 set to avoid the hardware treating it as see-through.
|
||||
if (vramPixel.Pack() == 0x0000)
|
||||
{
|
||||
vramPixel.R = 1;
|
||||
vramPixel.G = 1;
|
||||
vramPixel.B = 1;
|
||||
vramPixel.SemiTransparent = true;
|
||||
}
|
||||
|
||||
psxTex.ColorPalette.Add(vramPixel);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user