From 01b636f3e25e40ec5326b40240f61b86993e07f8 Mon Sep 17 00:00:00 2001 From: Jan Racek Date: Sun, 29 Mar 2026 16:14:30 +0200 Subject: [PATCH] ITS BROKEN --- Editor/Inspectors/PSXUIEditors.cs | 143 ++++++++++---------- Editor/PSXCutsceneEditor.cs | 71 ++++------ Plugins.meta | 2 +- Plugins/DotRecast.meta | 2 +- Plugins/DotRecast/DotRecast.Core.dll.meta | 2 +- Plugins/DotRecast/DotRecast.Recast.dll.meta | 2 +- Runtime/PSXCutsceneExporter.cs | 13 +- Runtime/PSXKeyframe.cs | 2 +- Runtime/PSXNavRegionBuilder.cs | 2 +- Runtime/PSXTrackType.cs | 2 +- 10 files changed, 114 insertions(+), 127 deletions(-) diff --git a/Editor/Inspectors/PSXUIEditors.cs b/Editor/Inspectors/PSXUIEditors.cs index 07c8220..cc3d3a2 100644 --- a/Editor/Inspectors/PSXUIEditors.cs +++ b/Editor/Inspectors/PSXUIEditors.cs @@ -1,46 +1,87 @@ using UnityEngine; using UnityEditor; using SplashEdit.RuntimeCode; +using System.Linq; namespace SplashEdit.EditorCode { // --- Scene Preview Gizmos --- - // These draw filled rectangles in the Scene view for WYSIWYG UI preview. + // A single canvas-level gizmo draws all children in hierarchy order + // so depth stacking is correct (last child in hierarchy renders on top). public static class PSXUIGizmos { [DrawGizmo(GizmoType.NonSelected | GizmoType.Selected)] - static void DrawBoxGizmo(PSXUIBox box, GizmoType gizmoType) + static void DrawCanvasGizmo(PSXCanvas canvas, GizmoType gizmoType) + { + RectTransform canvasRt = canvas.GetComponent(); + if (canvasRt == null) return; + + bool canvasSelected = (gizmoType & GizmoType.Selected) != 0; + + // Canvas border + Vector3[] canvasCorners = new Vector3[4]; + canvasRt.GetWorldCorners(canvasCorners); + Color border = canvasSelected ? Color.yellow : new Color(1, 1, 0, 0.3f); + Handles.DrawSolidRectangleWithOutline(canvasCorners, Color.clear, border); + + // Draw all children in hierarchy order (first child = back, last child = front) + var children = canvas.GetComponentsInChildren(true).Reverse(); + foreach (var child in children) + { + if (child == canvas.transform) continue; + bool childSelected = Selection.Contains(child.gameObject); + + var box = child.GetComponent(); + if (box != null) { DrawBox(box, childSelected); continue; } + + var image = child.GetComponent(); + if (image != null) { DrawImage(image, childSelected); continue; } + + var text = child.GetComponent(); + if (text != null) { DrawText(text, childSelected); continue; } + + var bar = child.GetComponent(); + if (bar != null) { DrawProgressBar(bar, childSelected); continue; } + } + + // Canvas label when selected + if (canvasSelected) + { + Vector2 res = PSXCanvas.PSXResolution; + Vector3 topMid = (canvasCorners[1] + canvasCorners[2]) * 0.5f; + string label = $"PSX Canvas: {canvas.CanvasName} ({res.x}x{res.y})"; + GUIStyle style = new GUIStyle(EditorStyles.boldLabel); + style.normal.textColor = Color.yellow; + Handles.Label(topMid, label, style); + } + } + + static void DrawBox(PSXUIBox box, bool selected) { RectTransform rt = box.GetComponent(); if (rt == null) return; Vector3[] corners = new Vector3[4]; rt.GetWorldCorners(corners); Color fill = box.BoxColor; - fill.a = (gizmoType & GizmoType.Selected) != 0 ? 0.8f : 0.5f; - Color border = Color.white; - border.a = (gizmoType & GizmoType.Selected) != 0 ? 1f : 0.4f; - Handles.DrawSolidRectangleWithOutline(corners, fill, border); + fill.a = selected ? 1f : 0.9f; + Color borderColor = selected ? Color.white : new Color(1, 1, 1, 0.5f); + Handles.DrawSolidRectangleWithOutline(corners, fill, borderColor); } - [DrawGizmo(GizmoType.NonSelected | GizmoType.Selected)] - static void DrawImageGizmo(PSXUIImage image, GizmoType gizmoType) + static void DrawImage(PSXUIImage image, bool selected) { RectTransform rt = image.GetComponent(); if (rt == null) return; Vector3[] corners = new Vector3[4]; rt.GetWorldCorners(corners); - bool selected = (gizmoType & GizmoType.Selected) != 0; - - // Draw texture preview if available if (image.SourceTexture != null) { Color tint = image.TintColor; - tint.a = selected ? 0.9f : 0.6f; + tint.a = selected ? 1f : 0.9f; Handles.DrawSolidRectangleWithOutline(corners, tint * 0.3f, tint); - // Draw texture in GUI overlay Handles.BeginGUI(); Vector2 min = HandleUtility.WorldToGUIPoint(corners[0]); Vector2 max = HandleUtility.WorldToGUIPoint(corners[2]); @@ -49,7 +90,7 @@ namespace SplashEdit.EditorCode Mathf.Abs(max.x - min.x), Mathf.Abs(max.y - min.y)); if (screenRect.width > 2 && screenRect.height > 2) { - GUI.color = new Color(tint.r, tint.g, tint.b, selected ? 0.9f : 0.5f); + GUI.color = new Color(tint.r, tint.g, tint.b, selected ? 1f : 0.9f); GUI.DrawTexture(screenRect, image.SourceTexture, ScaleMode.StretchToFill); GUI.color = Color.white; } @@ -57,27 +98,23 @@ namespace SplashEdit.EditorCode } else { - Color fill = new Color(0.4f, 0.4f, 0.8f, selected ? 0.4f : 0.2f); + Color fill = new Color(0.4f, 0.4f, 0.8f, selected ? 0.8f : 0.6f); Handles.DrawSolidRectangleWithOutline(corners, fill, Color.cyan); } } - [DrawGizmo(GizmoType.NonSelected | GizmoType.Selected)] - static void DrawTextGizmo(PSXUIText text, GizmoType gizmoType) + static void DrawText(PSXUIText text, bool selected) { RectTransform rt = text.GetComponent(); if (rt == null) return; Vector3[] corners = new Vector3[4]; rt.GetWorldCorners(corners); - bool selected = (gizmoType & GizmoType.Selected) != 0; - Color border = text.TextColor; - border.a = selected ? 1f : 0.5f; - Color fill = new Color(0, 0, 0, selected ? 0.3f : 0.1f); - Handles.DrawSolidRectangleWithOutline(corners, fill, border); + Color borderColor = text.TextColor; + borderColor.a = selected ? 1f : 0.7f; + Color fill = new Color(0, 0, 0, selected ? 0.6f : 0.4f); + Handles.DrawSolidRectangleWithOutline(corners, fill, borderColor); - // Pixel-perfect preview: render each glyph from the actual font bitmap - // at PS1 scale, using advance widths for proportional positioning. string label = string.IsNullOrEmpty(text.DefaultText) ? "[empty]" : text.DefaultText; PSXFontAsset font = text.GetEffectiveFont(); @@ -98,18 +135,14 @@ namespace SplashEdit.EditorCode float guiH = Mathf.Abs(botRight.y - topLeft.y); Color tintColor = text.TextColor; - tintColor.a = selected ? 1f : 0.7f; + tintColor.a = selected ? 1f : 0.8f; - // If we have a font bitmap, render pixel-perfect from the texture if (font != null && font.FontTexture != null && font.SourceFont != null) { Texture2D fontTex = font.FontTexture; int glyphsPerRow = font.GlyphsPerRow; - int texH = font.TextureHeight; - float cellScreenW = glyphW * psxPixelScale; float cellScreenH = glyphH * psxPixelScale; - // Get advance widths for proportional positioning float cursorX = guiX; GUI.color = tintColor; foreach (char ch in label) @@ -119,24 +152,19 @@ namespace SplashEdit.EditorCode int col = charIdx % glyphsPerRow; int row = charIdx / glyphsPerRow; - float advance = glyphW; // fallback + float advance = glyphW; if (font.AdvanceWidths != null && charIdx < font.AdvanceWidths.Length) - { advance = font.AdvanceWidths[charIdx]; - } if (ch != ' ') { - // UV rect for this glyph in the font bitmap float uvX = (float)(col * glyphW) / fontTex.width; float uvY = 1f - (float)((row + 1) * glyphH) / fontTex.height; float uvW = (float)glyphW / fontTex.width; float uvH = (float)glyphH / fontTex.height; - // Draw at advance width, not cell width - matches PS1 proportional rendering float spriteScreenW = advance * psxPixelScale; Rect screenRect = new Rect(cursorX, guiY, spriteScreenW, cellScreenH); - // UV: only show the portion of the cell that the advance covers float uvWScaled = uvW * (advance / glyphW); Rect uvRect = new Rect(uvX, uvY, uvWScaled, uvH); @@ -150,7 +178,6 @@ namespace SplashEdit.EditorCode } else { - // Fallback: use Unity's text rendering for system font int fSize = Mathf.Clamp(Mathf.RoundToInt(glyphH * psxPixelScale * 0.75f), 6, 72); GUIStyle style = new GUIStyle(EditorStyles.label); style.normal.textColor = tintColor; @@ -167,58 +194,30 @@ namespace SplashEdit.EditorCode Handles.EndGUI(); } - [DrawGizmo(GizmoType.NonSelected | GizmoType.Selected)] - static void DrawProgressBarGizmo(PSXUIProgressBar bar, GizmoType gizmoType) + static void DrawProgressBar(PSXUIProgressBar bar, bool selected) { RectTransform rt = bar.GetComponent(); if (rt == null) return; Vector3[] corners = new Vector3[4]; rt.GetWorldCorners(corners); - bool selected = (gizmoType & GizmoType.Selected) != 0; - - // Background Color bgColor = bar.BackgroundColor; - bgColor.a = selected ? 0.8f : 0.5f; - Handles.DrawSolidRectangleWithOutline(corners, bgColor, Color.white * (selected ? 1f : 0.4f)); + bgColor.a = selected ? 1f : 0.9f; + Handles.DrawSolidRectangleWithOutline(corners, bgColor, selected ? Color.white : new Color(1, 1, 1, 0.5f)); - // Fill portion float t = bar.InitialValue / 100f; if (t > 0.001f) { Vector3[] fillCorners = new Vector3[4]; - fillCorners[0] = corners[0]; // bottom-left - fillCorners[1] = corners[1]; // top-left - fillCorners[2] = Vector3.Lerp(corners[1], corners[2], t); // top-right (partial) - fillCorners[3] = Vector3.Lerp(corners[0], corners[3], t); // bottom-right (partial) + fillCorners[0] = corners[0]; + fillCorners[1] = corners[1]; + fillCorners[2] = Vector3.Lerp(corners[1], corners[2], t); + fillCorners[3] = Vector3.Lerp(corners[0], corners[3], t); Color fillColor = bar.FillColor; - fillColor.a = selected ? 0.9f : 0.6f; + fillColor.a = selected ? 1f : 0.9f; Handles.DrawSolidRectangleWithOutline(fillCorners, fillColor, Color.clear); } } - - [DrawGizmo(GizmoType.NonSelected | GizmoType.Selected)] - static void DrawCanvasGizmo(PSXCanvas canvas, GizmoType gizmoType) - { - RectTransform rt = canvas.GetComponent(); - if (rt == null) return; - Vector3[] corners = new Vector3[4]; - rt.GetWorldCorners(corners); - bool selected = (gizmoType & GizmoType.Selected) != 0; - Color border = selected ? Color.yellow : new Color(1, 1, 0, 0.3f); - Handles.DrawSolidRectangleWithOutline(corners, Color.clear, border); - - // Label - if (selected) - { - Vector2 res = PSXCanvas.PSXResolution; - Vector3 topMid = (corners[1] + corners[2]) * 0.5f; - string label = $"PSX Canvas: {canvas.CanvasName} ({res.x}x{res.y})"; - GUIStyle style = new GUIStyle(EditorStyles.boldLabel); - style.normal.textColor = Color.yellow; - Handles.Label(topMid, label, style); - } - } } /// /// Custom inspector for PSXCanvas component. diff --git a/Editor/PSXCutsceneEditor.cs b/Editor/PSXCutsceneEditor.cs index fb8f526..faef414 100644 --- a/Editor/PSXCutsceneEditor.cs +++ b/Editor/PSXCutsceneEditor.cs @@ -200,15 +200,16 @@ namespace SplashEdit.RuntimeCode else Debug.LogWarning("No active Scene View."); } } - else if (!isUITrack && (track.TrackType == PSXTrackType.ObjectPosition || track.TrackType == PSXTrackType.ObjectRotationY)) + else if (!isUITrack && (track.TrackType == PSXTrackType.ObjectPosition || track.TrackType == PSXTrackType.ObjectRotation)) { - if (GUILayout.Button("From Sel.", GUILayout.Width(70))) + // Capture from the named object in scene + if (!string.IsNullOrEmpty(track.ObjectName) && GUILayout.Button("From Object", GUILayout.Width(85))) { - var sel = Selection.activeGameObject; - if (sel != null) + var go = GameObject.Find(track.ObjectName); + if (go != null) kf.Value = track.TrackType == PSXTrackType.ObjectPosition - ? sel.transform.position : new Vector3(0, sel.transform.eulerAngles.y, 0); - else Debug.LogWarning("No GameObject selected."); + ? go.transform.position : go.transform.eulerAngles; + else Debug.LogWarning($"Object '{track.ObjectName}' not found in scene."); } } @@ -229,10 +230,10 @@ namespace SplashEdit.RuntimeCode kf.Value = new Vector3(active ? 1f : 0f, 0, 0); break; } - case PSXTrackType.ObjectRotationY: + case PSXTrackType.ObjectRotation: + case PSXTrackType.CameraRotation: { - float yRot = EditorGUILayout.FloatField("Y\u00b0", kf.Value.y); - kf.Value = new Vector3(0, yRot, 0); + kf.Value = EditorGUILayout.Vector3Field("Rotation\u00b0", kf.Value); break; } case PSXTrackType.UIProgress: @@ -297,17 +298,20 @@ namespace SplashEdit.RuntimeCode track.Keyframes.Add(new PSXKeyframe { Frame = frame, Value = val }); } } - else if (!isUITrack && (track.TrackType == PSXTrackType.ObjectPosition || track.TrackType == PSXTrackType.ObjectRotationY)) + else if (!isUITrack && (track.TrackType == PSXTrackType.ObjectPosition || track.TrackType == PSXTrackType.ObjectRotation)) { - if (GUILayout.Button("+ from Selected", GUILayout.Width(120))) + if (!string.IsNullOrEmpty(track.ObjectName)) { - var sel = Selection.activeGameObject; - Vector3 val = Vector3.zero; - if (sel != null) - val = track.TrackType == PSXTrackType.ObjectPosition - ? sel.transform.position : new Vector3(0, sel.transform.eulerAngles.y, 0); - int frame = track.Keyframes.Count > 0 ? track.Keyframes[track.Keyframes.Count - 1].Frame + 15 : 0; - track.Keyframes.Add(new PSXKeyframe { Frame = frame, Value = val }); + if (GUILayout.Button("+ from Object", GUILayout.Width(110))) + { + var go = GameObject.Find(track.ObjectName); + Vector3 val = Vector3.zero; + if (go != null) + val = track.TrackType == PSXTrackType.ObjectPosition + ? go.transform.position : go.transform.eulerAngles; + int frame = track.Keyframes.Count > 0 ? track.Keyframes[track.Keyframes.Count - 1].Frame + 15 : 0; + track.Keyframes.Add(new PSXKeyframe { Frame = frame, Value = val }); + } } } EditorGUILayout.EndHorizontal(); @@ -584,9 +588,9 @@ namespace SplashEdit.RuntimeCode if (_savedObjectPositions.ContainsKey(track.ObjectName ?? "")) initialVal = _savedObjectPositions[track.ObjectName]; break; - case PSXTrackType.ObjectRotationY: + case PSXTrackType.ObjectRotation: if (_savedObjectRotations.ContainsKey(track.ObjectName ?? "")) - initialVal = new Vector3(0, _savedObjectRotations[track.ObjectName].eulerAngles.y, 0); + initialVal = _savedObjectRotations[track.ObjectName].eulerAngles; break; case PSXTrackType.ObjectActive: if (_savedObjectActive.ContainsKey(track.ObjectName ?? "")) @@ -619,10 +623,10 @@ namespace SplashEdit.RuntimeCode if (go != null) go.transform.position = val; break; } - case PSXTrackType.ObjectRotationY: + case PSXTrackType.ObjectRotation: { var go = GameObject.Find(track.ObjectName); - if (go != null) go.transform.rotation = Quaternion.Euler(0, val.y, 0); + if (go != null) go.transform.rotation = Quaternion.Euler(val); break; } case PSXTrackType.ObjectActive: @@ -706,16 +710,6 @@ namespace SplashEdit.RuntimeCode { float rawT = frame / after.Frame; float t = ApplyInterpCurve(rawT, after.Interp); - - bool isRotation = track.TrackType == PSXTrackType.CameraRotation - || track.TrackType == PSXTrackType.ObjectRotationY; - if (isRotation) - { - return new Vector3( - Mathf.LerpAngle(initialValue.x, after.Value.x, t), - Mathf.LerpAngle(initialValue.y, after.Value.y, t), - Mathf.LerpAngle(initialValue.z, after.Value.z, t)); - } return Vector3.Lerp(initialValue, after.Value, t); } @@ -727,17 +721,8 @@ namespace SplashEdit.RuntimeCode float rawT2 = (frame - before.Frame) / span; float t2 = ApplyInterpCurve(rawT2, after.Interp); - // Use shortest-path angle interpolation for rotation tracks - bool isRot = track.TrackType == PSXTrackType.CameraRotation - || track.TrackType == PSXTrackType.ObjectRotationY; - if (isRot) - { - return new Vector3( - Mathf.LerpAngle(before.Value.x, after.Value.x, t2), - Mathf.LerpAngle(before.Value.y, after.Value.y, t2), - Mathf.LerpAngle(before.Value.z, after.Value.z, t2)); - } - + // Linear interpolation for all tracks including rotation. + // No shortest-path wrapping: a keyframe from 0 to 360 rotates the full circle. return Vector3.Lerp(before.Value, after.Value, t2); } diff --git a/Plugins.meta b/Plugins.meta index 4fd65df..ebe8443 100644 --- a/Plugins.meta +++ b/Plugins.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 +guid: afedc2b61a424884b90aeb912c54fe50 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Plugins/DotRecast.meta b/Plugins/DotRecast.meta index 68ac981..7da8667 100644 --- a/Plugins/DotRecast.meta +++ b/Plugins/DotRecast.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7 +guid: 873ed6988ff333343b8a535dcf4919b1 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Plugins/DotRecast/DotRecast.Core.dll.meta b/Plugins/DotRecast/DotRecast.Core.dll.meta index 5ee2066..37c849c 100644 --- a/Plugins/DotRecast/DotRecast.Core.dll.meta +++ b/Plugins/DotRecast/DotRecast.Core.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8 +guid: c732908a2854def459b9b5d59ea1d8c6 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Plugins/DotRecast/DotRecast.Recast.dll.meta b/Plugins/DotRecast/DotRecast.Recast.dll.meta index d13408e..9d93a17 100644 --- a/Plugins/DotRecast/DotRecast.Recast.dll.meta +++ b/Plugins/DotRecast/DotRecast.Recast.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9 +guid: ba66a74ed9417f2408b71435e402676b PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/PSXCutsceneExporter.cs b/Runtime/PSXCutsceneExporter.cs index 55b8a8e..0965e10 100644 --- a/Runtime/PSXCutsceneExporter.cs +++ b/Runtime/PSXCutsceneExporter.cs @@ -211,12 +211,15 @@ namespace SplashEdit.RuntimeCode writer.Write(rz); break; } - case PSXTrackType.ObjectRotationY: + case PSXTrackType.ObjectRotation: { - // Only Y rotation matters - writer.Write((short)0); - writer.Write(DegreesToAngleRaw(kf.Value.y)); - writer.Write((short)0); + // Full XYZ rotation in degrees -> pi-units + short rx = DegreesToAngleRaw(kf.Value.x); + short ry = DegreesToAngleRaw(kf.Value.y); + short rz = DegreesToAngleRaw(kf.Value.z); + writer.Write(rx); + writer.Write(ry); + writer.Write(rz); break; } case PSXTrackType.ObjectActive: diff --git a/Runtime/PSXKeyframe.cs b/Runtime/PSXKeyframe.cs index 5044a4e..d1b19b7 100644 --- a/Runtime/PSXKeyframe.cs +++ b/Runtime/PSXKeyframe.cs @@ -8,7 +8,7 @@ namespace SplashEdit.RuntimeCode /// Value interpretation depends on track type: /// CameraPosition / ObjectPosition: Unity world-space position (x, y, z) /// CameraRotation: Euler angles in degrees (x=pitch, y=yaw, z=roll) - /// ObjectRotationY: y component = rotation in degrees + /// ObjectRotation: y component = rotation in degrees /// ObjectActive: x component = 0.0 (inactive) or 1.0 (active) /// [Serializable] diff --git a/Runtime/PSXNavRegionBuilder.cs b/Runtime/PSXNavRegionBuilder.cs index 1057896..13177fe 100644 --- a/Runtime/PSXNavRegionBuilder.cs +++ b/Runtime/PSXNavRegionBuilder.cs @@ -312,7 +312,7 @@ namespace SplashEdit.RuntimeCode { foreach (var exporter in exporters) { - if (exporter.CollisionType == PSXCollisionType.Dynamic) + if (exporter.CollisionType == PSXCollisionType.Dynamic || exporter.CollisionType == PSXCollisionType.None) continue; MeshFilter mf = exporter.GetComponent(); diff --git a/Runtime/PSXTrackType.cs b/Runtime/PSXTrackType.cs index 79eed11..57ed628 100644 --- a/Runtime/PSXTrackType.cs +++ b/Runtime/PSXTrackType.cs @@ -5,7 +5,7 @@ namespace SplashEdit.RuntimeCode CameraPosition = 0, CameraRotation = 1, ObjectPosition = 2, - ObjectRotationY = 3, + ObjectRotation = 3, ObjectActive = 4, UICanvasVisible = 5, UIElementVisible = 6,