using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Rendering; using UnityEngine.SceneManagement; public abstract class PostProcessor { public const string SCENE_POST_PROCESS_NAME = "PostProcess"; private const string SCENE_SURFACE_FOLDER_NAME = "SurfaceData"; private const string SCENE_COMBINED_NAME = "Combined"; private const string GROUND_SPLIT_NAME = "GroundSplit"; protected const string SCENE_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessScenes"; protected const string PREFAB_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessPrefabs"; protected const string SURFACE_SHADER_PATH = "Assets/Code/Shaders/Scene/NLD_Scene_Surface.shader"; protected const string SCENE_ADDRESSABLE_GROUP = "PostProcessScenes"; protected const string PREFAB_ADDRESSABLE_GROUP = "PostProcessPrefabs"; private const int GROUND_SPLIT_WIDTH = 1024; private const int GROUND_SPLIT_HEIGHT = 1024; private const int UPP = 100; private readonly List sceneRoots = new() { "Main Camera", "TerrainGridSystem", "Art", "SceneManager", "MapEditor", "LevelEditor", "TGSPreview", "InputManager (Singleton)" }; protected string ProcessedResourceDir => $"{ResourcesBaseDir}/{PostProcessFolderName}"; protected abstract string ResourcesBaseDir { get; } private string SurfaceDir => $"{ProcessedResourceDir}/{SCENE_SURFACE_FOLDER_NAME}"; private string CombinedDir => $"{ProcessedResourceDir}/{SCENE_COMBINED_NAME}"; private string GroundSplitDir => $"{ProcessedResourceDir}/{GROUND_SPLIT_NAME}"; protected abstract string PostProcessFolderName { get; } protected PostProcessInstructor _processInstructor; private PostProcessInstructor _clonedProcessInstructor; //private NLDSceneManager NLDSceneManager { get; set; } //private NLDSceneManager _postProcessSceneManager; private bool HasClone { get; set; } protected abstract Object AssetToPostProcess { get; } protected string OriginAssetPath => AssetDatabase.GetAssetPath(AssetToPostProcess); protected abstract string OutputAssetPath { get; } private PostProcessInstructor PostProcessInstructor => HasClone ? _clonedProcessInstructor : _processInstructor; private Transform CombinedObjectsRoot { get { if (PostProcessInstructor == null || PostProcessInstructor.combinedObjectsRoot == null) { Debug.LogError($"找不到Art节点!"); return PostProcessInstructor.transform.parent; } return PostProcessInstructor.combinedObjectsRoot; } } private bool CheckPostProcessInstructor(PostProcessInstructor postProcessInstructor) { if (postProcessInstructor == null) { Debug.LogError("postProcessInstructor is null"); return false; } if (postProcessInstructor.processPrefab && (!PrefabUtility.IsPartOfAnyPrefab(postProcessInstructor) || EditorUtility.IsPersistent(postProcessInstructor))) { Debug.LogError("postProcessInstructor不在任何预制体内或者其不是预制体实例"); return false; } var currentScene = postProcessInstructor.gameObject.scene; var objs = currentScene.GetRootGameObjects(); foreach (var obj in objs) { if (!sceneRoots.Contains(obj.name)) { DebugUtil.LogWarning("{1} 场景有其他节点:{0},请检查节点规范", obj.name, currentScene.name); } } return true; } protected virtual void Init(PostProcessInstructor postProcessInstructor) { _processInstructor = postProcessInstructor; } public void PostProcess(PostProcessInstructor postProcessInstructor, bool destroyAfterPostProcess = false, bool defaultCameraParam = true) { if (!CheckPostProcessInstructor(postProcessInstructor)) return; Init(postProcessInstructor); HasClone = false; EditorSceneManager.SaveOpenScenes(); PrepareFolders(); //NLDSceneManager = CommonUtils.GetSceneRootComponent(Scene); //暂时先不渲染成面片(地面阴影还没法解决,以及一些与笔刷工具的耦合) //BakeSurfaceData(); _clonedProcessInstructor = Clone(); HasClone = true; if (_clonedProcessInstructor == null) { Debug.LogError("_clonedProcessInstructor is null!"); return; } BakeCombinedGameObject(); SplitGroundTexture(); SetFogPosition(); if (!defaultCameraParam) { SetCameraParam(); } RemoveRedundant(); DestroyUselessObject(); AfterAll(destroyAfterPostProcess); PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath); Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } protected abstract void AfterAll(bool destroyAfterPostProcess); public static PostProcessInstructor FindInstructorInScene(Scene scene) { var instructors = Object.FindObjectsOfType(); return instructors.FirstOrDefault(pi => pi.gameObject.scene == scene); } // 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 PrepareFolders() { AssetDatabase.DeleteAsset(ProcessedResourceDir); AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName); AssetDatabase.CreateFolder(ProcessedResourceDir, SCENE_SURFACE_FOLDER_NAME); AssetDatabase.CreateFolder(ProcessedResourceDir, SCENE_COMBINED_NAME); AssetDatabase.CreateFolder(ProcessedResourceDir, GROUND_SPLIT_NAME); } protected abstract PostProcessInstructor Clone(); //烘焙面片数据 // private void BakeSurfaceData() // { // if (NLDSceneManager == null) // return; // NLDSceneManager.allSceneSurfaceData.Clear(); // List renderInfos = new(); // var camera = Camera.main; // if (camera == null) // { // Debug.LogError("找不到主相机!请设置主相机的tag"); // return; // } // // foreach (var sObj in NLDSceneManager.sceneObjects) // { // if (sObj && sObj.renderInfo && !renderInfos.Contains(sObj.renderInfo)) // { // var brush = SceneBrushHelper.GetSceneBrushByRenderInfo(sObj.renderInfo); // if (!brush.SupportSurface) // continue; // renderInfos.Add(sObj.renderInfo); // var gameObject = brush.prefab; // var texPath = // EditorUtil.GetAssetUniquePath($"{SurfaceDir}/{gameObject.name}_surface.jpg", out var name); // Vector3 offset = Vector3.zero; // Vector3 scale = Vector3.one; // Quaternion rotation = Quaternion.identity; // var texture = gameObject.RenderToTexture(camera, texPath, ref offset, ref scale, ref rotation); // if (texture != null) // { // var material = new Material(AssetDatabase.LoadAssetAtPath(SURFACE_SHADER_PATH)) // { // mainTexture = texture // }; // material.enableInstancing = true; // var matPath = $"{SurfaceDir}/{gameObject.name}_surface.mat".Replace("\\", "/"); // EditorUtil.CreateAnUniqueAsset(material, matPath); // SceneSurfaceData surfaceData = new() // { // renderInfo = sObj.renderInfo, // materialPath = matPath, // cachedOffset = offset, // eulerAngleID = gameObject.transform.eulerAngles, // cachedRotation = rotation.eulerAngles, // cachedScale = scale, // valid = true, // }; // NLDSceneManager.allSceneSurfaceData.Add(surfaceData); // } // } // } // // //MarkSceneDirty(); // } //合并网格,贴图 private void BakeCombinedGameObject() { var art = CombinedObjectsRoot; var renderers = CollectRenderers(); var combinedRoot = new GameObject("CombinedRoot"); combinedRoot.transform.parent = art.transform; SceneCombineUtil.Instance.CombineGameObjects(renderers, combinedRoot.transform, CombinedDir, (int)PostProcessInstructor.postProcessTextureUnitSize); foreach (var renderer in renderers) { Object.DestroyImmediate(renderer.gameObject); } } private List CollectRenderers() { List allRenderers = new(); foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } var render = root.GetComponentsInChildren(); if (render is not { Length: > 0 }) { continue; } allRenderers.AddRange(render); } return allRenderers; } private void SplitGroundTexture() { var ground = PostProcessInstructor.ground; if (ground == null) return; var renderer = ground.GetComponent(); if (renderer == null) return; var mat = renderer.sharedMaterial; if (mat == null) return; var texture = mat.mainTexture as Texture2D; if (texture == null) return; var groundParent = Object.Instantiate(ground, ground.transform.parent, true); groundParent.name = "Ground"; var scaleRatioX = ground.transform.localScale.x * UPP / texture.width; var scaleRatioY = ground.transform.localScale.y * UPP / texture.height; var originPos = -new Vector3(ground.transform.localScale.x / 2f, ground.transform.localScale.y / 2f, 0); groundParent.transform.localScale = Vector3.one; //groundParent.transform.localScale = new Vector3(scaleRatio, scaleRatio, 1); var textures = SplitTextureUtil.SplitTexture(texture, GROUND_SPLIT_WIDTH, GROUND_SPLIT_HEIGHT, GroundSplitDir); var fixedWidth = (float)GROUND_SPLIT_WIDTH / UPP * scaleRatioX; var fixedHeight = (float)GROUND_SPLIT_HEIGHT / UPP * scaleRatioY; for (int row = 0; row < textures.GetLength(0); row++) { for (int column = 0; column < textures.GetLength(1); column++) { var splitTexture = textures[row, column]; var xScale = (float)splitTexture.width / UPP * scaleRatioX; var yScale = (float)splitTexture.height / UPP * scaleRatioY; var splitGround = Object.Instantiate(ground, groundParent.transform, true); Object.DestroyImmediate(splitGround.GetComponent()); var splitName = $"Ground_{row}_{column}"; splitGround.name = splitName; splitGround.transform.localScale = new Vector3(xScale, yScale, 1); var splitMat = Object.Instantiate(mat); splitMat.mainTexture = splitTexture; AssetDatabase.CreateAsset(splitMat, $"{GroundSplitDir}/{splitName}.mat"); splitGround.GetComponent().sharedMaterial = splitMat; splitGround.GetComponent().shadowCastingMode = ShadowCastingMode.Off; splitGround.transform.localPosition = originPos + new Vector3(column * fixedWidth + xScale / 2f, row * fixedHeight + yScale / 2f, 0); } } Object.DestroyImmediate(ground); Object.DestroyImmediate(groundParent.GetComponent()); Object.DestroyImmediate(groundParent.GetComponent()); var boxCollider = groundParent.gameObject.AddComponent(); boxCollider.size = new Vector3(200, 200, 1); boxCollider.center = new Vector3(0, 0, 0.5f); Object.DestroyImmediate(groundParent.GetComponent()); } private void SetFogPosition() { var fog = PostProcessInstructor.fog; if (fog == null) return; var transform = fog.transform; var position = transform.position; transform.position = new Vector3(position.x, position.y + 0.01f, position.z); } private void SetCameraParam() { var postScene = PostProcessInstructor.gameObject.scene; var postSceneName = postScene.name; var scene = _processInstructor.gameObject.scene; var sceneName = scene.name; foreach (var rootObj in scene.GetRootGameObjects()) { if (rootObj.CompareTag("MainCamera")) { CloneCameraToScene(rootObj, postScene); break; } } } private void RemoveRedundant() { Scene postScene = PostProcessInstructor.gameObject.scene; foreach (GameObject rootObj in postScene. GetRootGameObjects()) { EventSystem[] eventSystems = rootObj.GetComponentsInChildren(); if (null == eventSystems) continue; for (int i = 0; i < eventSystems.Length; ++i) { Object.DestroyImmediate(eventSystems[i].gameObject); } } } private void CloneCameraToScene(GameObject obj, Scene scene) { // 只更改transform var oldPosition = obj.transform.position; var oldRotation = obj.transform.rotation; GameObject oldCamera = null; foreach (var rootObj in scene.GetRootGameObjects()) { if (rootObj.CompareTag("MainCamera")) { oldCamera = rootObj; break; } } if (oldCamera == null) return; oldCamera.transform.position = oldPosition; oldCamera.transform.rotation = oldRotation; } private void DestroyUselessObject() { foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } Object.DestroyImmediate(root.gameObject, true); } Object.DestroyImmediate(PostProcessInstructor.gameObject, true); } }