using UnityEngine; using UnityEditor; using SplashEdit.RuntimeCode; using System.Linq; namespace SplashEdit.EditorCode { [CustomEditor(typeof(PSXObjectExporter))] [CanEditMultipleObjects] public class PSXObjectExporterEditor : UnityEditor.Editor { private SerializedProperty isActiveProp; private SerializedProperty bitDepthProp; private SerializedProperty luaFileProp; private SerializedProperty collisionTypeProp; private SerializedProperty staticColliderProp; private SerializedProperty exportCollisionMeshProp; private SerializedProperty customCollisionMeshProp; private SerializedProperty collisionLayerProp; private SerializedProperty generateNavigationProp; private MeshFilter meshFilter; private MeshRenderer meshRenderer; private int triangleCount; private int vertexCount; private bool showExport = true; private bool showCollision = true; private void OnEnable() { isActiveProp = serializedObject.FindProperty("isActive"); bitDepthProp = serializedObject.FindProperty("bitDepth"); luaFileProp = serializedObject.FindProperty("luaFile"); collisionTypeProp = serializedObject.FindProperty("collisionType"); staticColliderProp = serializedObject.FindProperty("staticCollider"); exportCollisionMeshProp = serializedObject.FindProperty("exportCollisionMesh"); customCollisionMeshProp = serializedObject.FindProperty("customCollisionMesh"); collisionLayerProp = serializedObject.FindProperty("collisionLayer"); generateNavigationProp = serializedObject.FindProperty("generateNavigation"); CacheMeshInfo(); } private void CacheMeshInfo() { var exporter = target as PSXObjectExporter; if (exporter == null) return; meshFilter = exporter.GetComponent(); meshRenderer = exporter.GetComponent(); if (meshFilter != null && meshFilter.sharedMesh != null) { triangleCount = meshFilter.sharedMesh.triangles.Length / 3; vertexCount = meshFilter.sharedMesh.vertexCount; } } public override void OnInspectorGUI() { serializedObject.Update(); DrawHeader(); EditorGUILayout.Space(4); if (!isActiveProp.boolValue) { EditorGUILayout.LabelField("Object will be skipped during export.", PSXEditorStyles.InfoBox); serializedObject.ApplyModifiedProperties(); return; } DrawMeshSummary(); PSXEditorStyles.DrawSeparator(6, 6); DrawExportSection(); PSXEditorStyles.DrawSeparator(6, 6); DrawCollisionSection(); PSXEditorStyles.DrawSeparator(6, 6); DrawActions(); serializedObject.ApplyModifiedProperties(); } private new void DrawHeader() { EditorGUILayout.BeginVertical(PSXEditorStyles.CardStyle); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(isActiveProp, GUIContent.none, GUILayout.Width(18)); var exporter = target as PSXObjectExporter; EditorGUILayout.LabelField(exporter.gameObject.name, PSXEditorStyles.CardHeaderStyle); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } private void DrawMeshSummary() { if (meshFilter == null || meshFilter.sharedMesh == null) { EditorGUILayout.HelpBox("No mesh on this object.", MessageType.Warning); return; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField($"{triangleCount} tris", PSXEditorStyles.RichLabel, GUILayout.Width(60)); EditorGUILayout.LabelField($"{vertexCount} verts", PSXEditorStyles.RichLabel, GUILayout.Width(70)); int subMeshCount = meshFilter.sharedMesh.subMeshCount; if (subMeshCount > 1) EditorGUILayout.LabelField($"{subMeshCount} submeshes", PSXEditorStyles.RichLabel, GUILayout.Width(90)); int matCount = meshRenderer != null ? meshRenderer.sharedMaterials.Length : 0; int textured = meshRenderer != null ? meshRenderer.sharedMaterials.Count(m => m != null && m.mainTexture != null) : 0; if (textured > 0) EditorGUILayout.LabelField($"{textured}/{matCount} textured", PSXEditorStyles.RichLabel); else EditorGUILayout.LabelField("untextured", PSXEditorStyles.RichLabel); EditorGUILayout.EndHorizontal(); } private void DrawExportSection() { showExport = EditorGUILayout.Foldout(showExport, "Export", true, PSXEditorStyles.FoldoutHeader); if (!showExport) return; EditorGUI.indentLevel++; EditorGUILayout.PropertyField(bitDepthProp, new GUIContent("Bit Depth")); 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))) AssetDatabase.OpenAsset(luaFileProp.objectReferenceValue); if (GUILayout.Button("Clear", EditorStyles.miniButtonRight, GUILayout.Width(50))) luaFileProp.objectReferenceValue = null; GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.BeginHorizontal(); GUILayout.Space(EditorGUI.indentLevel * 15); if (GUILayout.Button("Create Lua Script", EditorStyles.miniButton, GUILayout.Width(130))) CreateNewLuaScript(); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); } EditorGUI.indentLevel--; } private void DrawCollisionSection() { showCollision = EditorGUILayout.Foldout(showCollision, "Collision", true, PSXEditorStyles.FoldoutHeader); if (!showCollision) return; EditorGUI.indentLevel++; EditorGUILayout.PropertyField(collisionTypeProp, new GUIContent("Type")); var collType = (PSXCollisionType)collisionTypeProp.enumValueIndex; if (collType != PSXCollisionType.None) { EditorGUILayout.PropertyField(staticColliderProp, new GUIContent("Static")); bool isStatic = staticColliderProp.boolValue; if (isStatic) { EditorGUILayout.LabelField( "Baked into world collision mesh. No runtime cost.", PSXEditorStyles.RichLabel); } else { EditorGUILayout.LabelField( "Runtime AABB collider. Fires Lua collision events.", PSXEditorStyles.RichLabel); } EditorGUILayout.Space(2); EditorGUILayout.PropertyField(exportCollisionMeshProp, new GUIContent("Export Collision Mesh")); EditorGUILayout.PropertyField(customCollisionMeshProp, new GUIContent("Custom Mesh")); EditorGUILayout.PropertyField(collisionLayerProp, new GUIContent("Layer")); } EditorGUILayout.Space(4); EditorGUILayout.PropertyField(generateNavigationProp, new GUIContent("Generate Navigation")); EditorGUI.indentLevel--; } private void DrawActions() { EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Select Scene Exporter", EditorStyles.miniButton)) { var se = FindFirstObjectByType(); if (se != null) Selection.activeGameObject = se.gameObject; else EditorUtility.DisplayDialog("Not Found", "No PSXSceneExporter in scene.", "OK"); } EditorGUILayout.EndHorizontal(); } private void CreateNewLuaScript() { var exporter = target as PSXObjectExporter; string defaultName = exporter.gameObject.name.ToLower().Replace(" ", "_"); string path = EditorUtility.SaveFilePanelInProject( "Create Lua Script", defaultName + ".lua", "lua", "Create a new Lua script for this object"); if (string.IsNullOrEmpty(path)) return; string template = $"function onCreate(self)\nend\n\nfunction onUpdate(self, dt)\nend\n"; System.IO.File.WriteAllText(path, template); AssetDatabase.Refresh(); var luaFile = AssetDatabase.LoadAssetAtPath(path); if (luaFile != null) { luaFileProp.objectReferenceValue = luaFile; serializedObject.ApplyModifiedProperties(); } } } }