using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.IO; using Gameplay; using Sirenix.OdinInspector.Editor; using TGS; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; [CustomEditor(typeof(NLDSceneManager))] public partial class NLDSceneManagerInspector : OdinEditor { private const string SCENE_BRUSH_PATH = "Assets/Art/Scene/SceneBrush/Brush/Editor"; private const string SCENE_RENDER_INFO_PATH = "Assets/Art/Scene/SceneBrush/SceneRenderInfo"; private const string SURFACE_SHADER_PATH = "Assets/Code/Shaders/Scene/NLD_Scene_Surface.shader"; private const string GROUND_MATERIAL_PATH = "Assets/Art/Scene/Map/Mat"; private const float UPP = 100; private List _showBrushes = new(); private bool _isSelectingBrush; private bool _isCreatingBrush; private bool _isEditingScene; private bool _foldOutGround; private bool _foldOutBrush; private bool _foldOutSceneManager; private SceneBrush _currBrush; private string _brushName; private EBrushType _brushType; private string _searchStr; private EBrushType _searchType; private Vector2 _scrollPosition; private NLDSceneManager Target => target as NLDSceneManager; private Scene Scene => Target.gameObject.scene; private PreviewRenderUtility _previewRenderUtility; private GameObject Prefab => _currBrush != null ? _currBrush.prefab : null; private GameObject Ground { get { var obj = Target.Art.Find("Ground"); return obj != null ? obj.gameObject : null; } } private Texture2D _groundTex; private Texture2D GroundTexture { get { if (Ground == null) return null; if (_groundTex == null) { var renderer = Ground.GetComponent(); if (renderer != null) { var mat = renderer.sharedMaterial; if (mat != null) { _groundTex = mat.mainTexture as Texture2D; } } } return _groundTex; } set { if (value == null) { return; } if (_groundTex != value) { _groundTex = value; var mat = GetMapMaterial(value); Ground.GetComponent().sharedMaterial = mat; ResetGroundScale(); } } } private bool GroundValid => Ground != null && GroundTexture != null; private Vector2 GroundScale { get => Ground.transform.localScale; set { var currScale = Ground.transform.localScale; var xRatio = value.x / currScale.x; var yRatio = value.y / currScale.y; var ration = Mathf.Approximately(xRatio, 1) ? yRatio : xRatio; currScale *= ration; Ground.transform.localScale = new Vector3(currScale.x, currScale.y, 1); } } private TerrainGridSystem _tgs; private TerrainGridSystem TGS { get { if (_tgs == null) { var allTgs = FindObjectsOfType(); _tgs = allTgs.FirstOrDefault(t => t.gameObject.scene == Scene); } return _tgs; } } private Material GetMapMaterial(Texture2D texture) { var guids = AssetDatabase.FindAssets("t:Material", new[] { GROUND_MATERIAL_PATH }); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(path)) continue; var mat = AssetDatabase.LoadAssetAtPath(path); if (mat.mainTexture == texture) return mat; } var result = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")) { mainTexture = texture }; var savePath = $"{GROUND_MATERIAL_PATH}/{texture.name}.mat"; EditorUtil.CreateAnUniqueAsset(result, savePath); AssetDatabase.Refresh(); return result; } private void ResetGroundScale() { if (_groundTex != null) Ground.transform.localScale = new Vector3(_groundTex.width, _groundTex.height, 1) / UPP; } private void ValidateData() { if (_previewRenderUtility == null) { _previewRenderUtility = new PreviewRenderUtility(); _previewRenderUtility.camera.transform.rotation = Quaternion.identity; } } public override bool HasPreviewGUI() { ValidateData(); return true; } protected override void OnEnable() { base.OnEnable(); RefreshData(); Undo.undoRedoEvent += PreventEraserUnable; } private void PreventEraserUnable(in UndoRedoInfo undo) { if (undo.isRedo && undo.undoName == SceneBrush.BRUSH_DRAW_UNDO_NAME) { EditorSceneManager.SaveScene(Scene); } } protected override void OnDisable() { base.OnDisable(); if (_previewRenderUtility != null) { _previewRenderUtility.Cleanup(); } Undo.undoRedoEvent -= PreventEraserUnable; } private void RefreshData() { RefreshAllBrush(); RefreshBrushToShow(); } private void RefreshAllBrush() { SceneBrushHelper.RefreshAllBrush(); } private void RefreshBrushToShow() { _showBrushes.Clear(); foreach (var brush in SceneBrushHelper.AllBrushes) { if ((string.IsNullOrEmpty(_searchStr) || brush.name.IsStringContains(_searchStr) || brush.remarkName.IsStringContains(_searchStr)) && (_searchType == EBrushType.None || brush.brushType == _searchType)) { _showBrushes.Add(brush); } } } private bool CheckCreateBrush() { if (string.IsNullOrEmpty(_brushName)) { Debug.LogError("笔刷名为空!"); return false; } if (_brushType == EBrushType.None) { Debug.LogError("笔刷类型为空!"); return false; } foreach (var brush in SceneBrushHelper.AllBrushes) { var path = AssetDatabase.GetAssetPath(brush); if (Path.GetFileNameWithoutExtension(path) == _brushName) { Debug.LogError("当前笔刷名字已存在,请重新命名!"); return false; } } return true; } private bool CheckPreview() { return Prefab != null; } public override void OnPreviewGUI(Rect r, GUIStyle background) { if (Event.current.type == EventType.Repaint && CheckPreview()) { var bounds = Prefab.GetMaxBounds(); if (bounds == null) return; var realBounds = (Bounds)bounds; var y = realBounds.center.y; _previewRenderUtility.BeginPreview(r, background); _previewRenderUtility.camera.farClipPlane = 100; var height = realBounds.size.y + 1; var fov = _previewRenderUtility.camera.fieldOfView; var z = -(height / 2) / Mathf.Tan(Mathf.Deg2Rad * (fov / 2)); var cameraPos = new Vector3(0, y, z); _previewRenderUtility.camera.transform.position = cameraPos; var meshRenderers = Prefab.GetComponentsInChildren(); foreach (var msr in meshRenderers) { var matrix = msr.transform.localToWorldMatrix; matrix[0, 3] = 0; matrix[1, 3] = 0; matrix[2, 3] = 0; var materials = msr.sharedMaterials; var mesh = msr.GetComponent().sharedMesh; for (int i = 0; i < mesh.subMeshCount; i++) { if (i < materials.Length) _previewRenderUtility.DrawMesh(mesh, matrix, materials[i], i); } } _previewRenderUtility.camera.Render(); var result_render = _previewRenderUtility.EndPreview(); GUI.DrawTexture(r, result_render, ScaleMode.StretchToFill, false); } } public override void OnInspectorGUI() { base.OnInspectorGUI(); if (!Application.isPlaying) { MarkSceneDirty(); DrawGround(); DrawBrush(); //EditorGUILayout.Space(10); } DrawSceneManager(); } private void DrawGround() { if (Ground == null) return; _foldOutGround = EditorGUILayout.Foldout(_foldOutGround, "地面"); if (!_foldOutGround) { return; } GroundTexture = EditorGUILayout.ObjectField("地图贴图", GroundTexture, typeof(Texture2D), true) as Texture2D; if (GroundTexture != null) { EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); var scale = EditorGUILayout.Vector2Field("地图尺寸", GroundScale, GUILayout.MaxWidth(400), GUILayout.ExpandWidth(false)); if (EditorGUI.EndChangeCheck()) { GroundScale = scale; } if (GUILayout.Button("重置", GUILayout.Width(100))) { ResetGroundScale(); } EditorGUILayout.EndHorizontal(); // if (TGS && GUILayout.Button("铺满网格")) // { // var column = CommonUtils.GetHexCount(GroundScale.x, TGS.regularHexagonsWidth, false); // var row = CommonUtils.GetHexCount(GroundScale.y, _tgs.regularHexagonsWidth, true); // CommonUtils.CorrectTGSColumnAndRow(ref column, ref row); // TGS.columnCount = column; // TGS.rowCount = row; // } } } private void DrawBrush() { /*if (!GroundValid) return;*/ _foldOutBrush = EditorGUILayout.Foldout(_foldOutBrush, "笔刷"); if (!_foldOutBrush) { return; } DrawBrushSelecting(); DrawCreateBrush(); DrawBrushSettings(); } private void DrawBrushSelecting() { if (_isSelectingBrush) { RefreshBrushToShow(); _searchStr = EditorGUILayout.TextField("搜索笔刷", _searchStr); _searchType = (EBrushType)EditorGUILayout.EnumPopup("搜索笔刷类型", _searchType); var scrollViewHeight = Mathf.Min(200, 30 * (_showBrushes.Count + 1) + 10); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, GUI.skin.box, GUILayout.Height(scrollViewHeight)); EditorGUILayout.BeginHorizontal(GUI.skin.label, GUILayout.Height(20)); EditorGUILayout.LabelField("笔刷名", GUILayout.Width(120)); EditorGUILayout.LabelField("笔刷类型", GUILayout.Width(60)); EditorGUILayout.LabelField("备注", GUILayout.Width(100)); EditorGUILayout.EndHorizontal(); foreach (var brush in _showBrushes) { if (brush == _currBrush) GUI.color = Color.cyan; EditorGUILayout.BeginHorizontal(GUI.skin.box, GUILayout.Height(20)); EditorGUILayout.LabelField(brush.name, GUILayout.Width(120)); EditorGUILayout.LabelField(brush.brushType.ToString(), GUILayout.Width(60)); EditorGUILayout.LabelField(brush.remarkName, GUILayout.Width(100)); if (GUILayout.Button("选择")) { _currBrush = brush; _currBrush.OnSelected(Target, TGS); Repaint(); } GUI.color = Color.white; EditorGUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } if (SceneBrushHelper.AllBrushes.Count > 0) { if (!_isSelectingBrush && GUILayout.Button("选择笔刷")) { _isSelectingBrush = true; } if (_isSelectingBrush && GUILayout.Button("选择完毕")) { _scrollPosition = Vector2.zero; _isSelectingBrush = false; } } else { _isSelectingBrush = false; } } private void DrawCreateBrush() { if (!_isCreatingBrush) { if (GUILayout.Button("创建笔刷")) { _isCreatingBrush = true; Repaint(); } } else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); _brushName = EditorGUILayout.TextField("新建笔刷名", _brushName); _brushType = (EBrushType)EditorGUILayout.Popup("新建笔刷类型", (int)_brushType, SceneBrush.EBrushTypeStrings); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(); if (GUILayout.Button("创建")) { if (!CheckCreateBrush()) { return; } var renderInfoName = $"{_brushName}_renderInfo"; var renderInfoPath = $"{SCENE_RENDER_INFO_PATH}/{renderInfoName}.asset"; EditorUtil.CreateAnUniqueAsset(CreateInstance(), renderInfoPath); AssetDatabase.Refresh(); var renderInfo = AssetDatabase.LoadAssetAtPath(renderInfoPath); renderInfo.name = renderInfoName; var brushName = _brushName; var brushPath = $"{SCENE_BRUSH_PATH}/{brushName}.asset"; switch (_brushType) { case EBrushType.Tree: EditorUtil.CreateAnUniqueAsset(CreateInstance(), brushPath); break; case EBrushType.Building: EditorUtil.CreateAnUniqueAsset(CreateInstance(), brushPath); break; case EBrushType.Stuff: EditorUtil.CreateAnUniqueAsset(CreateInstance(), brushPath); break; default: break; } AssetDatabase.Refresh(); var brush = AssetDatabase.LoadAssetAtPath(brushPath); if (brush == null) { Debug.LogError("创建笔刷失败!"); return; } brush.renderInfo = renderInfo; brush.name = brushName; EditorUtility.SetDirty(renderInfo); EditorUtility.SetDirty(brush); _currBrush = brush; _currBrush.OnSelected(Target, TGS); _isCreatingBrush = false; RefreshData(); } if (GUILayout.Button("取消创建")) { _isCreatingBrush = false; } EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } } private void DrawBrushSettings() { if (_currBrush != null && !_isCreatingBrush) { GUI.enabled = false; EditorGUILayout.ObjectField("当前笔刷:", _currBrush, typeof(SceneBrush), false); EditorGUILayout.ObjectField("当前渲染信息:", _currBrush.renderInfo, typeof(SceneObjectRenderInfo), false); GUI.enabled = true; _currBrush.OnInspectorGUI(); if (_currBrush.Valid) { if (!_isEditingScene) { if (GUILayout.Button("开始编辑场景")) { _isEditingScene = true; } } else { if (GUILayout.Button("退出编辑场景")) { _isEditingScene = false; } } } if (GUILayout.Button("删除笔刷")) { if (EditorUtility.DisplayDialog("删除笔刷", "您确定要删除笔刷吗?删除后可能会导致场景中该笔刷绘制的物体失效!" + "\r\n如果确定要删除,删除后可以在SceneManager上点击删除失效场景对象。", "确认", "取消")) { _currBrush.OnDelete(); var path = AssetDatabase.GetAssetPath(_currBrush); AssetDatabase.DeleteAsset(path); _currBrush = null; RefreshData(); } } } } private void DrawSceneManager() { if (!GroundValid) return; _foldOutSceneManager = EditorGUILayout.Foldout(_foldOutSceneManager, "其他"); if (!_foldOutSceneManager) return; EditorGUI.BeginChangeCheck(); Target.UseAllSurface = EditorGUILayout.Toggle("开启面片", Target.UseAllSurface); if (EditorGUI.EndChangeCheck()) { if (!Application.isPlaying) MarkSceneDirty(); } if (!Application.isPlaying) { // if (GUILayout.Button("清除所有场景对象")) // { // if (EditorUtility.DisplayDialog("清除场景", "请问您确定要清除场景吗,清除后将不存在任何场景物件!", "确认", "取消")) // ClearAllSceneObject(); // } if (GUILayout.Button("保留失效场景对象")) { PreserveUselessSceneObjects(); } if (GUILayout.Button("清除失效场景对象")) { ClearUselessSceneObjects(); } if (!Scene.name.Contains(PostProcessor.SCENE_POST_PROCESS_NAME) && GUILayout.Button("场景后处理 (默认相机)")) { PostProcessScene(); } if (!Scene.name.Contains(PostProcessor.SCENE_POST_PROCESS_NAME) && GUILayout.Button("场景后处理 (保存相机更改)")) { PostProcessScene(false); } } } private void ClearAllSceneObject() { foreach (var sObj in Target.sceneObjects) { if (sObj) DestroyImmediate(sObj.gameObject); } Target.sceneObjects.Clear(); MarkSceneDirty(); } private void ClearUselessSceneObjects() { for (int i = Target.sceneObjects.Count - 1; i >= 0; i--) { var sObj = Target.sceneObjects[i]; if (sObj == null || sObj.renderInfo == null) { if (sObj != null) DestroyImmediate(sObj.gameObject); Target.sceneObjects.RemoveAt(i); } } ClearUselessSurfaceData(); MarkSceneDirty(); } private void PreserveUselessSceneObjects() { for (int i = Target.sceneObjects.Count - 1; i >= 0; i--) { var sObj = Target.sceneObjects[i]; if (sObj == null || sObj.renderInfo == null) { if (sObj != null) DestroyImmediate(sObj); Target.sceneObjects.RemoveAt(i); } } ClearUselessSurfaceData(); MarkSceneDirty(); } private void ClearUselessSurfaceData() { for (int i = Target.allSceneSurfaceData.Count - 1; i >= 0; i--) { var surfaceData = Target.allSceneSurfaceData[i]; if (surfaceData == null || surfaceData.renderInfo == null) { if (surfaceData != null) DeleteSurfaceData(surfaceData); Target.allSceneSurfaceData.RemoveAt(i); } } } private void DeleteSurfaceData(SceneSurfaceData surfaceData) { var mat = AssetDatabase.LoadAssetAtPath(surfaceData.materialPath); if (mat == null) { return; } AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(mat.mainTexture)); AssetDatabase.DeleteAsset(surfaceData.materialPath); } private void PostProcessScene(bool defaultCameraParam = true) { ObjectPostProcessorManager.Instance.PostProcess(Scene, false, defaultCameraParam); } private void MarkSceneDirty() { EditorSceneManager.MarkSceneDirty(Scene); } private void OnSceneGUI() { Handles.BeginGUI(); var sceneView = SceneView.lastActiveSceneView; var sceneViewPos = sceneView.position; if (GUI.Button(new Rect(sceneViewPos.width - 100, sceneViewPos.height - 50, 100, 30), "Scene归位")) { var mainCam = Camera.main; if (mainCam == null) return; if (Ground != null) sceneView.LookAt(Ground.transform.position, mainCam.transform.rotation); } Handles.EndGUI(); if (!_isEditingScene || _currBrush == null || !_currBrush.Valid || Application.isPlaying) return; var e = Event.current; _currBrush.OnSceneGUI(e); } }