using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using PhxhSDK.Editor.FileChecker; using Object = UnityEngine.Object; public abstract class PostProcessor { #region 常量 /// /// 后处理命名后缀 /// public const string SCENE_POST_PROCESS_NAME = "PostProcess"; /// /// 后处理后存放光照贴图和烘焙数据的文件夹命名 /// private const string LIGHT_BAKE_NAME = "LightMaps"; /// /// 后处理预制体根路径 /// protected const string PREFAB_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessPrefabs"; /// /// 场景资源分组路径 暂时舍弃没有使用Addressable /// protected const string SCENE_ADDRESSABLE_GROUP = "PostProcessScenes"; /// /// 预制体资源分组路径 暂时舍弃没有使用Addressable /// protected const string PREFAB_ADDRESSABLE_GROUP = "PostProcessPrefabs"; /// /// 区分剧情场景的文件夹前缀 /// private const string STORY_SCENE_PREFIX = "story_"; /// /// 切割地面贴图宽度 /// private const int GROUND_SPLIT_WIDTH = 1024; /// /// 切割地面贴图高度 /// private const int GROUND_SPLIT_HEIGHT = 1024; /// /// 像素 /// private const int UPP = 100; #endregion /// /// 烘焙后需要替换shader的渲染器列表 /// private readonly List needReplaceRenderers = new(); /// /// 烘焙后需要关闭产生阴影的渲染器列表 /// private readonly List allBakeRenderers = new(); /// /// 实时灯光 /// private readonly List realTimeLights = new(); /// /// 烘焙灯光 /// private readonly List bakedLights = new(); /// /// 检查规范节点 /// private readonly List sceneRoots = new() { "Main Camera", "TerrainGridSystem", "Art", "SceneManager", "MapEditor", "LevelEditor", "TGSPreview", "InputManager (Singleton)" }; /// /// 需要替换shader烘焙的节点 /// private readonly List bakeReplaceShaderRoot = new() { "CombinedRoot" }; /// /// 需要烘焙的其他地面节点 /// private readonly List groundRoot = new() { "Ground_2" }; /// /// 后处理文件夹路径 /// protected string ProcessedResourceDir => $"{ResourcesBaseDir}/{PostProcessFolderName}"; protected abstract string PostProcessFolderName { get; } protected abstract string ResourcesBaseDir { get; } protected abstract Object AssetToPostProcess { get; } protected abstract string OutputAssetPath { get; } private string LightBakeDir => $"{ProcessedResourceDir}/{LIGHT_BAKE_NAME}"; /// /// 是否完成复制场景 /// private bool HasClone { get; set; } /// /// 原始场景路径 /// private string OriginAssetPath => AssetDatabase.GetAssetPath(AssetToPostProcess); protected PostProcessInstructor _processInstructor; private PostProcessInstructor _clonedProcessInstructor; private PostProcessInstructor PostProcessInstructor => HasClone ? _clonedProcessInstructor : _processInstructor; /// /// 用于处理后处理场景中的预制体 /// private Transform _unpackedCombinedObjectsRoot; private Transform CombinedObjectsRoot { get { if (_unpackedCombinedObjectsRoot != null && HasClone) return _unpackedCombinedObjectsRoot; if (PostProcessInstructor == null || PostProcessInstructor.combinedObjectsRoot == null) { Debug.LogError("找不到Art节点!"); return PostProcessInstructor.transform.parent; } return PostProcessInstructor.combinedObjectsRoot; } } protected abstract PostProcessInstructor Clone(); protected abstract void AfterAll(bool destroyAfterPostProcess); protected virtual void Init(PostProcessInstructor postProcessInstructor) { _processInstructor = postProcessInstructor; } public static PostProcessInstructor FindInstructorInScene(Scene scene) { var instructors = Object.FindObjectsOfType(); return instructors.FirstOrDefault(pi => pi.gameObject.scene == scene); } #region 后处理多节点 /// /// 后处理多节点场景 不烘焙 预制体不解包 /// /// /// public void PostProcessMore(PostProcessInstructor[] postProcessInstructors, bool destroyAfterPostProcess = false) { if (postProcessInstructors.Length <= 0) return; var firstPostProcessInstructor = postProcessInstructors[0]; if (!CheckPostProcessInstructor(firstPostProcessInstructor)) return; Init(firstPostProcessInstructor); HasClone = false; EditorSceneManager.SaveOpenScenes(); // 创建文件夹 AssetDatabase.DeleteAsset(ProcessedResourceDir); AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName); // 复制场景为后处理场景 Clone(); // 在后处理场景中重新获取 PostProcessInstructor 组 PostProcessInstructor[] newPostProcessInstructors = Object.FindObjectsOfType(); int index = 0; foreach (PostProcessInstructor postProcessInstructor in newPostProcessInstructors) { // DebugUtil.LogError($"重新获取的postProcessInstructor属于场景:{postProcessInstructor.gameObject.scene.name}"); _processInstructor = postProcessInstructor; if (PostProcessInstructor == null) { // DebugUtil.LogError($"PostProcessInstructor为空,克隆参数:{HasClone}"); continue; } BakeCombinedGameObject(index); ++index; if (!PostProcessInstructor.enableSplitGround) { // 关闭切割地面 给地面添加BoxCollider GroundAddBoxCollider(); ChangeMapTextureAndMatPath(); } else { SplitGroundTexture(); } SetFogPosition(); RemoveRedundant(); HideCamera(); DestroyUselessObject(); } AfterAll(destroyAfterPostProcess); PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath); Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } #endregion #region 普通后处理 /// /// 后处理 /// /// /// /// 是否为默认相机 public void PostProcess(PostProcessInstructor postProcessInstructor, bool destroyAfterPostProcess = false, bool defaultCameraParam = true) { if (!CheckPostProcessInstructor(postProcessInstructor)) return; Init(postProcessInstructor); HasClone = false; EditorSceneManager.SaveOpenScenes(); PrepareFolders(); // 复制 ProcessInstructor 组件 _clonedProcessInstructor = Clone(); HasClone = true; if (_clonedProcessInstructor == null) { Debug.LogError("_clonedProcessInstructor is null!"); return; } GameObject combineRootObj = BakeCombinedGameObject(0); _PrepareForBake(combineRootObj.transform); // 如果Art为预制体先进行解包 UnpackPostSceneArt(); if (!PostProcessInstructor.enableSplitGround) { // 关闭切割地面 给地面添加BoxCollider GroundAddBoxCollider(); ChangeMapTextureAndMatPath(); } else { SplitGroundTexture(); } BakeLightmaps(); SetFogPosition(); if (!defaultCameraParam) { SetCameraParam(); } UnpackPostSceneArt(); RemoveRedundant(); HideCamera(); SetTimeline(); DestroyUselessObject(); AfterAll(destroyAfterPostProcess); PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath); Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); CheckScene(); } #endregion private void CheckScene() { try { // 获取当前活动的场景 Scene activeScene = EditorSceneManager.GetActiveScene(); if (!activeScene.IsValid()) { Debug.LogWarning("[PostProcessor] 当前没有有效的活动场景,跳过场景检查"); return; } Debug.Log($"[PostProcessor] 开始检查场景: {activeScene.name}"); // 获取场景根对象 GameObject[] rootObjects = activeScene.GetRootGameObjects(); if (rootObjects == null || rootObjects.Length == 0) { Debug.LogWarning($"[PostProcessor] 场景 {activeScene.name} 没有根对象,跳过检查"); return; } // 获取所有场景文件检查器 var sceneCheckers = GetAllSceneFileCheckers(); if (sceneCheckers.Count == 0) { Debug.LogWarning("[PostProcessor] 没有找到任何场景文件检查器"); return; } Debug.Log($"[PostProcessor] 找到 {sceneCheckers.Count} 个场景检查器,开始执行检查..."); // 执行所有检查器 bool hasErrors = false; List errorMessages = new List(); foreach (var checker in sceneCheckers) { try { Debug.Log($"[PostProcessor] 执行检查器: {checker.CheckerName}"); // 执行检查 object checkResult = checker.CheckScene(activeScene, rootObjects); if (checkResult != null) { hasErrors = true; string errorMsg = $"[{checker.CheckerName}] {checkResult}"; errorMessages.Add(errorMsg); Debug.LogWarning($"[PostProcessor] 检查失败: {errorMsg}"); } else { Debug.Log($"[PostProcessor] 检查器 {checker.CheckerName} 检查通过"); } } catch (System.Exception ex) { hasErrors = true; string errorMsg = $"[{checker.CheckerName}] 检查器执行异常: {ex.Message}"; errorMessages.Add(errorMsg); Debug.LogError($"[PostProcessor] {errorMsg}"); } } // 输出检查结果汇总 if (hasErrors) { Debug.LogError($"[PostProcessor] 场景 {activeScene.name} 检查完成,发现 {errorMessages.Count} 个问题:"); foreach (var errorMsg in errorMessages) { Debug.LogError($"[PostProcessor] • {errorMsg}"); } } else { Debug.Log($"[PostProcessor] 场景 {activeScene.name} 检查完成,所有检查器均通过"); } } catch (System.Exception ex) { Debug.LogError($"[PostProcessor] 场景检查过程中发生异常: {ex.Message}\n{ex.StackTrace}"); } } /// /// 获取所有场景文件检查器 /// private List GetAllSceneFileCheckers() { var sceneCheckers = new List(); try { // 获取所有检查器 var allCheckers = FileCheckerRegistry.Instance.GetAllCheckers(); // 筛选出场景文件检查器 foreach (var checker in allCheckers) { if (checker is BaseSceneFileChecker sceneChecker) { sceneCheckers.Add(sceneChecker); } } } catch (System.Exception ex) { Debug.LogError($"[PostProcessor] 获取场景文件检查器时发生异常: {ex.Message}"); } return sceneCheckers; } private void _PrepareForBake(Transform combineRootParent) { if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking) return; for (var i = 0; i < combineRootParent.childCount; ++i) { var child = combineRootParent.GetChild(i); if (child == null) continue; var meshRenderer = child.GetComponent(); if (meshRenderer == null) { continue; } if (meshRenderer.shadowCastingMode == ShadowCastingMode.On) { meshRenderer.shadowCastingMode = ShadowCastingMode.On; meshRenderer.staticShadowCaster = true; meshRenderer.sharedMaterial.doubleSidedGI = true; } } } 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)) { Debug.LogWarning($"{currentScene.name} 场景有其他节点:{obj.name},请检查节点规范"); } } return true; } private void PrepareFolders() { AssetDatabase.DeleteAsset(ProcessedResourceDir); AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName); } /// /// 合并网格、贴图 /// private GameObject BakeCombinedGameObject(int index) { Transform art = CombinedObjectsRoot; string outputPath = $"{ProcessedResourceDir}"; // 收集渲染器 var renderers = MeshCombineUtils.CollectRenderers(PostProcessInstructor.rootsToCombine); Debug.Log($"合批前收集到 {renderers.Count} 个渲染器"); var prefix = GetScenePrefix(); var combinedRoot = new GameObject("CombinedRoot"); combinedRoot.transform.parent = art.transform; DebugUtil.Log($"合并节点是:{GameObjectUtils.GetFullPath(combinedRoot)},输出路径:{outputPath}," + $"单位贴图尺寸:{(int)PostProcessInstructor.postProcessTextureUnitSize}"); try { if (PostProcessInstructor.enableBatchGrouping) { // 按照距离对渲染器进行分组 Dictionary> rendererGroups = MeshCombineUtils.GroupRenderersByProximity(renderers, PostProcessInstructor.batchGroupDistance); SceneCombineUtil.Instance.CombineGameObjectsByGroups(rendererGroups, combinedRoot.transform, outputPath, (int)PostProcessInstructor.postProcessTextureUnitSize, PostProcessInstructor.sampleMaterials, prefix); } else { // 使用原有的合批方式 SceneCombineUtil.Instance.CombineGameObjects(renderers, combinedRoot.transform, outputPath, (int)PostProcessInstructor.postProcessTextureUnitSize, PostProcessInstructor.sampleMaterials, prefix); } // 如果是剧情场景,重命名生成的所有物体添加前缀 if (!string.IsNullOrEmpty(prefix)) { var combined = combinedRoot.GetComponentsInChildren(); foreach (var trans in combined) { if (trans != combinedRoot.transform && !trans.name.StartsWith(prefix)) { trans.name = prefix + trans.name; } } } foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } Object.DestroyImmediate(root.gameObject); } } catch (System.Exception ex) { Debug.LogError($"合批过程中发生错误: {ex.Message}\n{ex.StackTrace}"); // 如果合批失败,保留原始物体 if (combinedRoot != null) { Object.DestroyImmediate(combinedRoot); } } return combinedRoot; } #region 地面处理 /// /// 修改地面贴图及材质的路径 用于打包 /// private void ChangeMapTextureAndMatPath() { var ground = PostProcessInstructor.ground; if (_unpackedCombinedObjectsRoot != null && HasClone) { ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject; } GroundUtils.ChangeMapTextureAndMatPath(ground, ProcessedResourceDir, GetScenePrefix(), groundRoot, CombinedObjectsRoot, _unpackedCombinedObjectsRoot); } /// /// 给地面添加BoxCollider /// private void GroundAddBoxCollider() { var ground = PostProcessInstructor.ground; if (_unpackedCombinedObjectsRoot != null && HasClone) { ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject; } if (ground != null) { // DebugUtil.LogError("获取的地面节点全路径:" + GameObjectUtils.GetFullPath(ground)); SceneUtils.AddBoxColliderToGround(ground); } else { Debug.LogError("场景地面节点不存在,无法添加BoxCollider"); } // 移除下层地面的碰撞体 var ground_2 = GameObject.Find("Ground_2"); if (ground_2 != null) { var collider = ground_2.GetComponent(); if (collider != null) { Object.DestroyImmediate(collider); } } } /// /// 切割地面 /// private void SplitGroundTexture() { var ground = PostProcessInstructor.ground; if (ground == null) return; GroundUtils.SplitGroundTexture(ground, ProcessedResourceDir, GetScenePrefix(), GROUND_SPLIT_WIDTH, GROUND_SPLIT_HEIGHT); } #endregion /// /// 设置雾的位置 /// private void SetFogPosition() { SceneUtils.SetFogPosition(PostProcessInstructor.fog); } /// /// 销毁多余eventSystems /// private void RemoveRedundant() { SceneUtils.RemoveRedundantEventSystems(PostProcessInstructor.gameObject.scene); } private void DestroyUselessObject() { foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } Object.DestroyImmediate(root.gameObject, true); } Object.DestroyImmediate(PostProcessInstructor.gameObject, true); var designerObj = GameObject.Find("Designer"); if (designerObj != null) { Object.DestroyImmediate(designerObj, true); } } /// /// 检测是否为剧情场景 /// /// private bool IsStoryScene() { var sceneName = PostProcessInstructor.gameObject.scene.name; return sceneName.Contains(STORY_SCENE_PREFIX); } private string GetScenePrefix() { return IsStoryScene() ? STORY_SCENE_PREFIX : ""; } #region 保存相机参数 private void SetCameraParam() { var postScene = PostProcessInstructor.gameObject.scene; var scene = _processInstructor.gameObject.scene; foreach (var rootObj in scene.GetRootGameObjects()) { if (rootObj.CompareTag("MainCamera")) { CloneCameraToScene(rootObj, postScene); break; } } } 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 HideCamera() { var scene = PostProcessInstructor.gameObject.scene; var cameras = Object.FindObjectsOfType(); foreach (var camera in cameras) { if (camera.gameObject.scene == scene) { camera.gameObject.SetActive(false); // Debug.Log($"已隐藏相机: {camera.gameObject.name}"); } } EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); EditorSceneManager.SaveOpenScenes(); } #endregion #region 烘焙 /// /// 烘焙灯光贴图的方法 /// private void BakeLightmaps() { // 检查是否开启了烘焙选项 if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking) return; Debug.Log("开始灯光烘焙过程..."); // 1. 从CombinedObjectsRoot下查找灯光 改为烘焙灯光 LightmapUtils.PrepareLight(CombinedObjectsRoot, realTimeLights, bakedLights); // 2. 处理参与烘焙的节点 LightmapUtils.PreProcessBake(bakeReplaceShaderRoot, CombinedObjectsRoot, true, true, allBakeRenderers, needReplaceRenderers); LightmapUtils.PreProcessBake(groundRoot, CombinedObjectsRoot, false, false, allBakeRenderers, needReplaceRenderers); ProcessGround(3000); Debug.Log($"总共收集到 {needReplaceRenderers.Count} 个渲染器需要替换shader"); Debug.Log($"总共收集到 {allBakeRenderers.Count} 个渲染器参与烘焙"); // 3.替换Shader为支持光照贴图的Shader MaterialUtils.ReplaceShaderWithLightmapShader(needReplaceRenderers); // 4. 开始烘焙 LightmapUtils.Bake(SceneManager.GetActiveScene()); // 5. 关闭所有烘焙物体的阴影投射 foreach (var renderer in allBakeRenderers) { if (renderer != null) renderer.shadowCastingMode = ShadowCastingMode.Off; } // 6. 打开实时灯光 LightmapUtils.RevertLight(realTimeLights, bakedLights); // 7.还原地面RenderQueue ProcessGround(2000); // 8. 复制光照烘焙文件到目标路径 LightmapUtils.CopyLightmapFiles(SceneManager.GetActiveScene(), LightBakeDir); // 9. 保存场景 EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); EditorSceneManager.SaveOpenScenes(); Debug.Log("灯光烘焙处理完成"); } private void ProcessGround(int renderQueue) { if (PostProcessInstructor.ground != null) { SceneUtils.SetGroundRenderQueue(PostProcessInstructor.ground, renderQueue); } } private void UnpackPostSceneArt() { if (CombinedObjectsRoot != null && PrefabUtility.IsPartOfPrefabInstance(CombinedObjectsRoot.gameObject)) { // 保存对象的引用 _unpackedCombinedObjectsRoot = CombinedObjectsRoot; SceneUtils.UnpackPrefab(CombinedObjectsRoot.gameObject); } } #endregion #region 剧情Timeline /// /// 设置剧情timeline /// private void SetTimeline() { /*GameObject goTimeline = PostProcessInstructor.timeline; if (null == goTimeline) return; PlayableDirector playableDirector = goTimeline.GetComponent(); if (null == playableDirector) return; if (null == playableDirector.playableAsset) return; string timelinePath = AssetDatabase.GetAssetPath(playableDirector.playableAsset); string outputPath = $"{ProcessedResourceDir}/{playableDirector.playableAsset.name}.playable"; AssetDatabase.CopyAsset(timelinePath, outputPath); PlayableAsset newPlayableAsset = (PlayableAsset)AssetDatabase.LoadAssetAtPath(outputPath, typeof(PlayableAsset)); playableDirector.playableAsset = newPlayableAsset; EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene()); AssetDatabase.SaveAssets(); AssetDatabase.Refresh();*/ } #endregion }