From 24d0c1fa0742082c7a06d02b7c0dac49c5d8f192 Mon Sep 17 00:00:00 2001 From: Jan Racek Date: Fri, 27 Mar 2026 18:31:35 +0100 Subject: [PATCH] bugfixes --- Editor/Core/PCdrvSerialHost.cs | 15 +- Editor/Core/PSXConsoleWindow.cs | 129 ++++--- Editor/Core/SplashControlPanel.cs | 328 ++++++------------ Editor/Core/SplashSettings.cs | 34 +- Editor/Inspectors/PSXComponentEditors.cs | 104 ++---- Editor/Inspectors/PSXComponentEditors2.cs | 268 ++++++++++++++ .../Inspectors/PSXComponentEditors2.cs.meta | 2 + Editor/Inspectors/PSXUIEditors.cs | 198 +++++++---- Editor/PSXCutsceneEditor.cs | 6 +- Editor/PSXMenuItems.cs | 2 +- Editor/PSXNavRegionEditor.cs | 2 +- Editor/PSXObjectExporterEditor.cs | 10 +- Editor/PSXSceneExporterEditor.cs | 83 +---- Editor/PSXTriggerBoxEditor.cs | 20 +- Editor/QuantizedPreviewWindow.cs | 25 +- Editor/VramEditorWindow.cs | 105 ++---- .../{PSXAudioSource.cs => PSXAudioClip.cs} | 4 +- Runtime/PSXAudioClip.cs.meta | 2 + Runtime/PSXAudioEvent.cs | 2 +- Runtime/PSXAudioSource.cs.meta | 2 - Runtime/PSXCutsceneExporter.cs | 2 +- Runtime/PSXObjectExporter.cs | 3 +- Runtime/PSXPortalLink.cs | 5 +- Runtime/PSXRoom.cs | 5 +- Runtime/PSXSceneExporter.cs | 6 +- Runtime/PSXSceneWriter.cs | 2 +- Runtime/PSXTexture2D.cs | 24 ++ 27 files changed, 779 insertions(+), 609 deletions(-) create mode 100644 Editor/Inspectors/PSXComponentEditors2.cs create mode 100644 Editor/Inspectors/PSXComponentEditors2.cs.meta rename Runtime/{PSXAudioSource.cs => PSXAudioClip.cs} (93%) create mode 100644 Runtime/PSXAudioClip.cs.meta delete mode 100644 Runtime/PSXAudioSource.cs.meta diff --git a/Editor/Core/PCdrvSerialHost.cs b/Editor/Core/PCdrvSerialHost.cs index 6263331..51218a2 100644 --- a/Editor/Core/PCdrvSerialHost.cs +++ b/Editor/Core/PCdrvSerialHost.cs @@ -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 log, Action 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 } } } diff --git a/Editor/Core/PSXConsoleWindow.cs b/Editor/Core/PSXConsoleWindow.cs index 549a97e..972ca8a 100644 --- a/Editor/Core/PSXConsoleWindow.cs +++ b/Editor/Core/PSXConsoleWindow.cs @@ -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(); + 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(); } } } diff --git a/Editor/Core/SplashControlPanel.cs b/Editor/Core/SplashControlPanel.cs index 5e0a6b9..2c7ddfc 100644 --- a/Editor/Core/SplashControlPanel.cs +++ b/Editor/Core/SplashControlPanel.cs @@ -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(); @@ -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); diff --git a/Editor/Core/SplashSettings.cs b/Editor/Core/SplashSettings.cs index e7974db..8ecf1c3 100644 --- a/Editor/Core/SplashSettings.cs +++ b/Editor/Core/SplashSettings.cs @@ -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 --- /// /// 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" }; diff --git a/Editor/Inspectors/PSXComponentEditors.cs b/Editor/Inspectors/PSXComponentEditors.cs index d790e63..573245d 100644 --- a/Editor/Inspectors/PSXComponentEditors.cs +++ b/Editor/Inspectors/PSXComponentEditors.cs @@ -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(); - } } } diff --git a/Editor/Inspectors/PSXComponentEditors2.cs b/Editor/Inspectors/PSXComponentEditors2.cs new file mode 100644 index 0000000..2c7dda6 --- /dev/null +++ b/Editor/Inspectors/PSXComponentEditors2.cs @@ -0,0 +1,268 @@ +using UnityEngine; +using UnityEditor; +using SplashEdit.RuntimeCode; + +namespace SplashEdit.EditorCode +{ + /// + /// Custom inspector for PSXAudioClip component. + /// + [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(); + } + } + + /// + /// Custom inspector for PSXPlayer component. + /// + [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(); + } + } + + /// + /// Custom inspector for PSXPortalLink component. + /// + [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(); + } + } + + /// + /// Custom inspector for PSXRoom component. + /// + [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(); + } + } +} diff --git a/Editor/Inspectors/PSXComponentEditors2.cs.meta b/Editor/Inspectors/PSXComponentEditors2.cs.meta new file mode 100644 index 0000000..b80c8e0 --- /dev/null +++ b/Editor/Inspectors/PSXComponentEditors2.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3fd7a7bcc7d0ff841b158f2744d48010 \ No newline at end of file diff --git a/Editor/Inspectors/PSXUIEditors.cs b/Editor/Inspectors/PSXUIEditors.cs index 5926b2d..07c8220 100644 --- a/Editor/Inspectors/PSXUIEditors.cs +++ b/Editor/Inspectors/PSXUIEditors.cs @@ -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(true).Length; int boxCount = canvas.GetComponentsInChildren(true).Length; @@ -263,14 +270,16 @@ namespace SplashEdit.EditorCode int progressCount = canvas.GetComponentsInChildren(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(); diff --git a/Editor/PSXCutsceneEditor.cs b/Editor/PSXCutsceneEditor.cs index d3eda35..fb8f526 100644 --- a/Editor/PSXCutsceneEditor.cs +++ b/Editor/PSXCutsceneEditor.cs @@ -92,7 +92,7 @@ namespace SplashEdit.RuntimeCode var exporters = Object.FindObjectsByType(FindObjectsSortMode.None); foreach (var e in exporters) exporterNames.Add(e.gameObject.name); - var audioSources = Object.FindObjectsByType(FindObjectsSortMode.None); + var audioSources = Object.FindObjectsByType(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(FindObjectsSortMode.None); + var audioSources = Object.FindObjectsByType(FindObjectsSortMode.None); foreach (var a in audioSources) if (!string.IsNullOrEmpty(a.ClipName) && a.Clip != null) _audioClipCache[a.ClipName] = a.Clip; diff --git a/Editor/PSXMenuItems.cs b/Editor/PSXMenuItems.cs index e7c5305..ce63d1c 100644 --- a/Editor/PSXMenuItems.cs +++ b/Editor/PSXMenuItems.cs @@ -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(); diff --git a/Editor/PSXNavRegionEditor.cs b/Editor/PSXNavRegionEditor.cs index 446caf0..ef1263e 100644 --- a/Editor/PSXNavRegionEditor.cs +++ b/Editor/PSXNavRegionEditor.cs @@ -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("Nav Region Builder"); diff --git a/Editor/PSXObjectExporterEditor.cs b/Editor/PSXObjectExporterEditor.cs index ccc8db3..40224e9 100644 --- a/Editor/PSXObjectExporterEditor.cs +++ b/Editor/PSXObjectExporterEditor.cs @@ -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(); if (se != null) diff --git a/Editor/PSXSceneExporterEditor.cs b/Editor/PSXSceneExporterEditor.cs index c53a360..39974f1 100644 --- a/Editor/PSXSceneExporterEditor.cs +++ b/Editor/PSXSceneExporterEditor.cs @@ -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( - $"Preview: {fogNearUnity:F1} – {fogFarUnity:F1} units | " + - $"GTE: {8000f / (density * 3f):F0} – {8000f / density:F0} SZ", + $"GTE range: {fogNearUnity:F1} - {fogFarUnity:F1} units | " + + $"{8000f / (density * 3f):F0} - {8000f / density:F0} SZ", 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(); + var exporters = FindObjectsByType(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().Length; + int triggerBoxes = FindObjectsByType(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; - } } } diff --git a/Editor/PSXTriggerBoxEditor.cs b/Editor/PSXTriggerBoxEditor.cs index 91221ba..ad1511f 100644 --- a/Editor/PSXTriggerBoxEditor.cs +++ b/Editor/PSXTriggerBoxEditor.cs @@ -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(); } diff --git a/Editor/QuantizedPreviewWindow.cs b/Editor/QuantizedPreviewWindow.cs index c93e5c5..aee0c60 100644 --- a/Editor/QuantizedPreviewWindow.cs +++ b/Editor/QuantizedPreviewWindow.cs @@ -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"); diff --git a/Editor/VramEditorWindow.cs b/Editor/VramEditorWindow.cs index ea77bd8..85f5b42 100644 --- a/Editor/VramEditorWindow.cs +++ b/Editor/VramEditorWindow.cs @@ -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 prohibitedAreas = new List(); 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); } /// @@ -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; diff --git a/Runtime/PSXAudioSource.cs b/Runtime/PSXAudioClip.cs similarity index 93% rename from Runtime/PSXAudioSource.cs rename to Runtime/PSXAudioClip.cs index ef813a9..aa75d89 100644 --- a/Runtime/PSXAudioSource.cs +++ b/Runtime/PSXAudioClip.cs @@ -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. /// - [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 = ""; diff --git a/Runtime/PSXAudioClip.cs.meta b/Runtime/PSXAudioClip.cs.meta new file mode 100644 index 0000000..e2eb88c --- /dev/null +++ b/Runtime/PSXAudioClip.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0da2803235be654438e86fe9d9a954d4 \ No newline at end of file diff --git a/Runtime/PSXAudioEvent.cs b/Runtime/PSXAudioEvent.cs index 020397a..92c38b9 100644 --- a/Runtime/PSXAudioEvent.cs +++ b/Runtime/PSXAudioEvent.cs @@ -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).")] diff --git a/Runtime/PSXAudioSource.cs.meta b/Runtime/PSXAudioSource.cs.meta deleted file mode 100644 index fda9cdb..0000000 --- a/Runtime/PSXAudioSource.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 3c4c3feb30e8c264baddc3a5e774473b \ No newline at end of file diff --git a/Runtime/PSXCutsceneExporter.cs b/Runtime/PSXCutsceneExporter.cs index 85d1466..55b8a8e 100644 --- a/Runtime/PSXCutsceneExporter.cs +++ b/Runtime/PSXCutsceneExporter.cs @@ -45,7 +45,7 @@ namespace SplashEdit.RuntimeCode BinaryWriter writer, PSXCutsceneClip[] cutscenes, PSXObjectExporter[] exporters, - PSXAudioSource[] audioSources, + PSXAudioClip[] audioSources, float gteScaling, out long cutsceneTableStart, Action log = null) diff --git a/Runtime/PSXObjectExporter.cs b/Runtime/PSXObjectExporter.cs index 78c7f2d..47b99fc 100644 --- a/Runtime/PSXObjectExporter.cs +++ b/Runtime/PSXObjectExporter.cs @@ -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; diff --git a/Runtime/PSXPortalLink.cs b/Runtime/PSXPortalLink.cs index fec8181..3283204 100644 --- a/Runtime/PSXPortalLink.cs +++ b/Runtime/PSXPortalLink.cs @@ -26,8 +26,11 @@ namespace SplashEdit.RuntimeCode void OnDrawGizmos() { + var exporter = FindFirstObjectByType(); + 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)); diff --git a/Runtime/PSXRoom.cs b/Runtime/PSXRoom.cs index a3933f0..5405ccb 100644 --- a/Runtime/PSXRoom.cs +++ b/Runtime/PSXRoom.cs @@ -56,6 +56,9 @@ namespace SplashEdit.RuntimeCode void OnDrawGizmos() { + var exporter = FindFirstObjectByType(); + 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 diff --git a/Runtime/PSXSceneExporter.cs b/Runtime/PSXSceneExporter.cs index 15da127..2245d30 100644 --- a/Runtime/PSXSceneExporter.cs +++ b/Runtime/PSXSceneExporter.cs @@ -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; /// @@ -120,7 +122,7 @@ namespace SplashEdit.RuntimeCode // Collect components _interactables = FindObjectsByType(FindObjectsSortMode.None); - _audioSources = FindObjectsByType(FindObjectsSortMode.None); + _audioSources = FindObjectsByType(FindObjectsSortMode.None); _triggerBoxes = FindObjectsByType(FindObjectsSortMode.None); // Collect UI image textures for VRAM packing alongside 3D textures diff --git a/Runtime/PSXSceneWriter.cs b/Runtime/PSXSceneWriter.cs index c13ce88..ad11d4e 100644 --- a/Runtime/PSXSceneWriter.cs +++ b/Runtime/PSXSceneWriter.cs @@ -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; diff --git a/Runtime/PSXTexture2D.cs b/Runtime/PSXTexture2D.cs index 53ec79d..a203b0d 100644 --- a/Runtime/PSXTexture2D.cs +++ b/Runtime/PSXTexture2D.cs @@ -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); }