2024-01-15 20:25:49 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
|
using UnityEditor.SceneManagement;
|
|
|
|
|
|
using UnityEngine;
|
2025-07-15 19:36:35 +08:00
|
|
|
|
using UnityEngine.Playables;
|
2024-04-28 18:19:18 +08:00
|
|
|
|
using UnityEngine.Rendering;
|
2024-01-15 20:25:49 +08:00
|
|
|
|
using UnityEngine.SceneManagement;
|
2025-07-22 16:33:20 +08:00
|
|
|
|
using PhxhSDK.Editor.FileChecker;
|
2025-04-14 15:51:02 +08:00
|
|
|
|
using Object = UnityEngine.Object;
|
2025-08-07 16:19:51 +08:00
|
|
|
|
using UnityEngine.Timeline;
|
|
|
|
|
|
using Cysharp.Threading.Tasks;
|
2026-01-13 13:20:29 +08:00
|
|
|
|
using PhxhSDK;
|
|
|
|
|
|
using UnityEditorGameObjectUtility = UnityEditor.GameObjectUtility;
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
|
|
|
|
|
public abstract class PostProcessor
|
|
|
|
|
|
{
|
2025-07-15 15:01:48 +08:00
|
|
|
|
#region 常量
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 后处理命名后缀
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
public const string SCENE_POST_PROCESS_NAME = "PostProcess";
|
2025-05-08 11:59:40 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 后处理后存放光照贴图和烘焙数据的文件夹命名
|
|
|
|
|
|
/// </summary>
|
2025-04-07 18:55:27 +08:00
|
|
|
|
private const string LIGHT_BAKE_NAME = "LightMaps";
|
2024-10-22 16:10:43 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 后处理预制体根路径
|
|
|
|
|
|
/// </summary>
|
2024-11-04 15:01:58 +08:00
|
|
|
|
protected const string PREFAB_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessPrefabs";
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 场景资源分组路径 暂时舍弃没有使用Addressable
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
protected const string SCENE_ADDRESSABLE_GROUP = "PostProcessScenes";
|
2025-05-08 11:59:40 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 预制体资源分组路径 暂时舍弃没有使用Addressable
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
protected const string PREFAB_ADDRESSABLE_GROUP = "PostProcessPrefabs";
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 区分剧情场景的文件夹前缀
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const string STORY_SCENE_PREFIX = "story_";
|
2025-04-03 14:08:04 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 切割地面贴图宽度
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
private const int GROUND_SPLIT_WIDTH = 1024;
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 切割地面贴图高度
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const int GROUND_SPLIT_HEIGHT = 1024;
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 像素
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const int UPP = 100;
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-15 15:01:48 +08:00
|
|
|
|
#endregion
|
2025-04-17 19:25:12 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 烘焙后需要替换shader的渲染器列表
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly List<Renderer> needReplaceRenderers = new();
|
2025-04-17 19:25:12 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 烘焙后需要关闭产生阴影的渲染器列表
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly List<Renderer> allBakeRenderers = new();
|
2025-04-17 19:25:12 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 实时灯光
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly List<Light> realTimeLights = new();
|
2025-04-17 19:25:12 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 烘焙灯光
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private readonly List<Light> bakedLights = new();
|
2025-04-14 15:51:02 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查规范节点
|
|
|
|
|
|
/// </summary>
|
2024-10-22 16:10:43 +08:00
|
|
|
|
private readonly List<string> sceneRoots = new()
|
2025-04-07 18:55:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
"Main Camera", "TerrainGridSystem", "Art", "SceneManager", "MapEditor", "LevelEditor", "TGSPreview",
|
|
|
|
|
|
"InputManager (Singleton)"
|
|
|
|
|
|
};
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-04-17 19:25:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 需要替换shader烘焙的节点
|
|
|
|
|
|
/// </summary>
|
2025-05-08 11:59:40 +08:00
|
|
|
|
private readonly List<string> bakeReplaceShaderRoot = new() { "CombinedRoot" };
|
2025-04-14 15:51:02 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// 需要烘焙的其他地面节点
|
2025-04-14 15:51:02 +08:00
|
|
|
|
/// </summary>
|
2025-05-08 11:59:40 +08:00
|
|
|
|
private readonly List<string> groundRoot = new() { "Ground_2" };
|
2024-10-22 16:10:43 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 后处理文件夹路径
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
protected string ProcessedResourceDir => $"{ResourcesBaseDir}/{PostProcessFolderName}";
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected abstract string PostProcessFolderName { get; }
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected abstract string ResourcesBaseDir { get; }
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected abstract Object AssetToPostProcess { get; }
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected abstract string OutputAssetPath { get; }
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-09-09 16:31:48 +08:00
|
|
|
|
private string LightBakeDir => $"{ProcessedResourceDir}";
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否完成复制场景
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
private bool HasClone { get; set; }
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 原始场景路径
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private string OriginAssetPath => AssetDatabase.GetAssetPath(AssetToPostProcess);
|
2024-01-16 18:37:02 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected PostProcessInstructor _processInstructor;
|
2024-10-22 16:10:43 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
private PostProcessInstructor _clonedProcessInstructor;
|
2024-01-16 18:37:02 +08:00
|
|
|
|
|
2024-01-15 20:25:49 +08:00
|
|
|
|
private PostProcessInstructor PostProcessInstructor => HasClone ? _clonedProcessInstructor : _processInstructor;
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 用于处理后处理场景中的预制体
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Transform _unpackedCombinedObjectsRoot;
|
2025-04-18 14:04:30 +08:00
|
|
|
|
|
2024-01-17 13:44:26 +08:00
|
|
|
|
private Transform CombinedObjectsRoot
|
2024-01-15 20:25:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2025-04-18 14:04:30 +08:00
|
|
|
|
if (_unpackedCombinedObjectsRoot != null && HasClone)
|
|
|
|
|
|
return _unpackedCombinedObjectsRoot;
|
|
|
|
|
|
|
2024-01-17 13:44:26 +08:00
|
|
|
|
if (PostProcessInstructor == null || PostProcessInstructor.combinedObjectsRoot == null)
|
2024-01-15 20:25:49 +08:00
|
|
|
|
{
|
2025-04-07 14:18:07 +08:00
|
|
|
|
Debug.LogError("找不到Art节点!");
|
2024-01-15 20:25:49 +08:00
|
|
|
|
return PostProcessInstructor.transform.parent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-17 13:44:26 +08:00
|
|
|
|
return PostProcessInstructor.combinedObjectsRoot;
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected abstract PostProcessInstructor Clone();
|
2024-09-24 16:57:21 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
protected abstract void AfterAll(bool destroyAfterPostProcess);
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
|
|
|
|
|
protected virtual void Init(PostProcessInstructor postProcessInstructor)
|
|
|
|
|
|
{
|
|
|
|
|
|
_processInstructor = postProcessInstructor;
|
2025-09-09 16:31:48 +08:00
|
|
|
|
PostProcessInstructor.DicNumber.Clear();
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
public static PostProcessInstructor FindInstructorInScene(Scene scene)
|
|
|
|
|
|
{
|
|
|
|
|
|
var instructors = Object.FindObjectsOfType<PostProcessInstructor>();
|
|
|
|
|
|
return instructors.FirstOrDefault(pi => pi.gameObject.scene == scene);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 17:25:12 +08:00
|
|
|
|
#region 后处理多节点
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 后处理多节点场景 不烘焙 预制体不解包
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="postProcessInstructors"></param>
|
|
|
|
|
|
/// <param name="destroyAfterPostProcess"></param>
|
|
|
|
|
|
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 组
|
2025-07-15 15:01:48 +08:00
|
|
|
|
PostProcessInstructor[] newPostProcessInstructors = Object.FindObjectsOfType<PostProcessInstructor>();
|
|
|
|
|
|
int index = 0;
|
|
|
|
|
|
foreach (PostProcessInstructor postProcessInstructor in newPostProcessInstructors)
|
2025-06-16 17:25:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
// DebugUtil.LogError($"重新获取的postProcessInstructor属于场景:{postProcessInstructor.gameObject.scene.name}");
|
|
|
|
|
|
_processInstructor = postProcessInstructor;
|
|
|
|
|
|
|
|
|
|
|
|
if (PostProcessInstructor == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// DebugUtil.LogError($"PostProcessInstructor为空,克隆参数:{HasClone}");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-15 15:01:48 +08:00
|
|
|
|
BakeCombinedGameObject(index);
|
|
|
|
|
|
++index;
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
|
|
|
|
|
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 普通后处理
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 后处理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="postProcessInstructor"></param>
|
|
|
|
|
|
/// <param name="destroyAfterPostProcess"></param>
|
|
|
|
|
|
/// <param name="defaultCameraParam">是否为默认相机</param>
|
2025-08-07 16:19:51 +08:00
|
|
|
|
public async void PostProcess(PostProcessInstructor postProcessInstructor, bool destroyAfterPostProcess = false,
|
2025-04-07 18:55:27 +08:00
|
|
|
|
bool defaultCameraParam = true)
|
2024-01-15 20:25:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!CheckPostProcessInstructor(postProcessInstructor))
|
|
|
|
|
|
return;
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2024-01-15 20:25:49 +08:00
|
|
|
|
Init(postProcessInstructor);
|
|
|
|
|
|
HasClone = false;
|
|
|
|
|
|
EditorSceneManager.SaveOpenScenes();
|
|
|
|
|
|
PrepareFolders();
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
// 复制 ProcessInstructor 组件
|
2024-01-15 20:25:49 +08:00
|
|
|
|
_clonedProcessInstructor = Clone();
|
2024-01-16 14:23:05 +08:00
|
|
|
|
HasClone = true;
|
2024-01-15 20:25:49 +08:00
|
|
|
|
if (_clonedProcessInstructor == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("_clonedProcessInstructor is null!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-07-15 15:01:48 +08:00
|
|
|
|
GameObject combineRootObj = BakeCombinedGameObject(0);
|
2025-05-15 19:27:10 +08:00
|
|
|
|
|
|
|
|
|
|
_PrepareForBake(combineRootObj.transform);
|
2025-08-07 16:19:51 +08:00
|
|
|
|
await SetTimeline();
|
2025-05-08 11:59:40 +08:00
|
|
|
|
// 如果Art为预制体先进行解包
|
|
|
|
|
|
UnpackPostSceneArt();
|
2025-04-16 15:28:21 +08:00
|
|
|
|
|
2025-08-26 19:00:29 +08:00
|
|
|
|
if(!postProcessInstructor.isTerrain)
|
2025-05-08 11:59:40 +08:00
|
|
|
|
{
|
2025-08-26 19:00:29 +08:00
|
|
|
|
if (!PostProcessInstructor.enableSplitGround)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 关闭切割地面 给地面添加BoxCollider
|
|
|
|
|
|
GroundAddBoxCollider();
|
|
|
|
|
|
ChangeMapTextureAndMatPath();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
SplitGroundTexture();
|
|
|
|
|
|
}
|
2025-05-08 11:59:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-08-29 16:22:02 +08:00
|
|
|
|
// Terrain 地形处理
|
|
|
|
|
|
DebugUtil.Log("开始处理 Terrain 地形");
|
|
|
|
|
|
ChangeMapTextureAndMatPathForTerrain();
|
2025-05-08 11:59:40 +08:00
|
|
|
|
}
|
2025-04-16 15:28:21 +08:00
|
|
|
|
|
2025-08-29 16:22:02 +08:00
|
|
|
|
// 根据地面类型选择不同的烘焙方法
|
|
|
|
|
|
if (PostProcessInstructor.isTerrain)
|
|
|
|
|
|
{
|
|
|
|
|
|
BakeLightmapsForTerrain();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
BakeLightmaps();
|
|
|
|
|
|
}
|
2025-04-18 16:33:53 +08:00
|
|
|
|
|
2024-01-15 20:25:49 +08:00
|
|
|
|
SetFogPosition();
|
2024-10-22 16:10:43 +08:00
|
|
|
|
if (!defaultCameraParam)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetCameraParam();
|
|
|
|
|
|
}
|
2025-04-07 18:55:27 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
UnpackPostSceneArt();
|
2024-10-25 15:30:02 +08:00
|
|
|
|
RemoveRedundant();
|
2025-05-20 13:02:09 +08:00
|
|
|
|
HideCamera();
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2024-01-16 14:23:05 +08:00
|
|
|
|
DestroyUselessObject();
|
2024-01-15 20:25:49 +08:00
|
|
|
|
AfterAll(destroyAfterPostProcess);
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2024-11-05 14:33:43 +08:00
|
|
|
|
PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath);
|
2024-01-16 18:37:02 +08:00
|
|
|
|
Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}");
|
2024-01-26 16:02:49 +08:00
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
|
AssetDatabase.Refresh();
|
2025-07-22 16:33:20 +08:00
|
|
|
|
|
|
|
|
|
|
CheckScene();
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-16 17:25:12 +08:00
|
|
|
|
#endregion
|
2025-06-18 19:46:49 +08:00
|
|
|
|
|
2025-07-22 16:33:20 +08:00
|
|
|
|
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<string> errorMessages = new List<string>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var checker in sceneCheckers)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log($"[PostProcessor] 执行检查器: {checker.CheckerName}");
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-22 16:33:20 +08:00
|
|
|
|
// 执行检查
|
|
|
|
|
|
object checkResult = checker.CheckScene(activeScene, rootObjects);
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-22 16:33:20 +08:00
|
|
|
|
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}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取所有场景文件检查器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private List<BaseSceneFileChecker> GetAllSceneFileCheckers()
|
|
|
|
|
|
{
|
|
|
|
|
|
var sceneCheckers = new List<BaseSceneFileChecker>();
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-22 16:33:20 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取所有检查器
|
|
|
|
|
|
var allCheckers = FileCheckerRegistry.Instance.GetAllCheckers();
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-22 16:33:20 +08:00
|
|
|
|
// 筛选出场景文件检查器
|
|
|
|
|
|
foreach (var checker in allCheckers)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (checker is BaseSceneFileChecker sceneChecker)
|
|
|
|
|
|
{
|
|
|
|
|
|
sceneCheckers.Add(sceneChecker);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"[PostProcessor] 获取场景文件检查器时发生异常: {ex.Message}");
|
|
|
|
|
|
}
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-22 16:33:20 +08:00
|
|
|
|
return sceneCheckers;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-15 19:27:10 +08:00
|
|
|
|
private void _PrepareForBake(Transform combineRootParent)
|
|
|
|
|
|
{
|
2025-05-20 13:02:09 +08:00
|
|
|
|
if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2025-05-15 19:27:10 +08:00
|
|
|
|
for (var i = 0; i < combineRootParent.childCount; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
var child = combineRootParent.GetChild(i);
|
|
|
|
|
|
if (child == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
var meshRenderer = child.GetComponent<MeshRenderer>();
|
|
|
|
|
|
if (meshRenderer == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2025-05-15 19:27:10 +08:00
|
|
|
|
if (meshRenderer.shadowCastingMode == ShadowCastingMode.On)
|
|
|
|
|
|
{
|
|
|
|
|
|
meshRenderer.shadowCastingMode = ShadowCastingMode.On;
|
|
|
|
|
|
meshRenderer.staticShadowCaster = true;
|
2025-09-09 15:32:26 +08:00
|
|
|
|
if (meshRenderer.sharedMaterial != null)
|
|
|
|
|
|
meshRenderer.sharedMaterial.doubleSidedGI = true;
|
2025-05-15 19:27:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
private bool CheckPostProcessInstructor(PostProcessInstructor postProcessInstructor)
|
2024-01-15 20:25:49 +08:00
|
|
|
|
{
|
2025-05-08 11:59:40 +08:00
|
|
|
|
if (postProcessInstructor == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("postProcessInstructor is null");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
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))
|
|
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
Debug.LogWarning($"{currentScene.name} 场景有其他节点:{obj.name},请检查节点规范");
|
2025-05-08 11:59:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-01-15 20:25:49 +08:00
|
|
|
|
|
|
|
|
|
|
private void PrepareFolders()
|
|
|
|
|
|
{
|
|
|
|
|
|
AssetDatabase.DeleteAsset(ProcessedResourceDir);
|
2024-01-16 14:23:05 +08:00
|
|
|
|
AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName);
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 合并网格、贴图
|
|
|
|
|
|
/// </summary>
|
2025-07-15 15:01:48 +08:00
|
|
|
|
private GameObject BakeCombinedGameObject(int index)
|
2024-01-15 20:25:49 +08:00
|
|
|
|
{
|
2025-07-15 15:01:48 +08:00
|
|
|
|
Transform art = CombinedObjectsRoot;
|
|
|
|
|
|
string outputPath = $"{ProcessedResourceDir}";
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-04-07 14:18:07 +08:00
|
|
|
|
// 收集渲染器
|
2025-04-25 15:07:22 +08:00
|
|
|
|
var renderers = MeshCombineUtils.CollectRenderers(PostProcessInstructor.rootsToCombine);
|
2025-04-07 14:18:07 +08:00
|
|
|
|
Debug.Log($"合批前收集到 {renderers.Count} 个渲染器");
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-04-07 14:18:07 +08:00
|
|
|
|
var prefix = GetScenePrefix();
|
2024-01-15 20:25:49 +08:00
|
|
|
|
var combinedRoot = new GameObject("CombinedRoot");
|
|
|
|
|
|
combinedRoot.transform.parent = art.transform;
|
2026-01-13 13:20:29 +08:00
|
|
|
|
DebugUtil.Log($"合并节点是:{combinedRoot.GetFullPath()},输出路径:{outputPath}," +
|
2025-06-16 17:25:12 +08:00
|
|
|
|
$"单位贴图尺寸:{(int)PostProcessInstructor.postProcessTextureUnitSize}");
|
2025-04-07 14:18:07 +08:00
|
|
|
|
try
|
2025-04-01 16:59:45 +08:00
|
|
|
|
{
|
2025-04-07 14:18:07 +08:00
|
|
|
|
if (PostProcessInstructor.enableBatchGrouping)
|
|
|
|
|
|
{
|
2025-08-01 17:40:16 +08:00
|
|
|
|
var limitRange = MeshCombineUtils.CalculateCurrentHexagonMapBounds(); // 可以根据实际地图调整
|
|
|
|
|
|
DebugUtil.Log($"边界宽:{limitRange.x},高:{limitRange.y}");
|
2025-04-07 14:18:07 +08:00
|
|
|
|
// 按照距离对渲染器进行分组
|
2025-08-01 17:40:16 +08:00
|
|
|
|
var rendererGroups = MeshCombineUtils.GroupRenderersByProximity(
|
|
|
|
|
|
renderers, PostProcessInstructor.batchGroupDistance, limitRange.x, limitRange.y);
|
2025-04-07 14:18:07 +08:00
|
|
|
|
SceneCombineUtil.Instance.CombineGameObjectsByGroups(rendererGroups, combinedRoot.transform,
|
2025-06-16 17:25:12 +08:00
|
|
|
|
outputPath,
|
2025-04-07 14:18:07 +08:00
|
|
|
|
(int)PostProcessInstructor.postProcessTextureUnitSize,
|
|
|
|
|
|
PostProcessInstructor.sampleMaterials, prefix);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 使用原有的合批方式
|
2025-06-16 17:25:12 +08:00
|
|
|
|
SceneCombineUtil.Instance.CombineGameObjects(renderers, combinedRoot.transform, outputPath,
|
2025-04-07 14:18:07 +08:00
|
|
|
|
(int)PostProcessInstructor.postProcessTextureUnitSize,
|
|
|
|
|
|
PostProcessInstructor.sampleMaterials, prefix);
|
|
|
|
|
|
}
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-04-07 14:18:07 +08:00
|
|
|
|
// 如果是剧情场景,重命名生成的所有物体添加前缀
|
|
|
|
|
|
if (!string.IsNullOrEmpty(prefix))
|
|
|
|
|
|
{
|
|
|
|
|
|
var combined = combinedRoot.GetComponentsInChildren<Transform>();
|
|
|
|
|
|
foreach (var trans in combined)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (trans != combinedRoot.transform && !trans.name.StartsWith(prefix))
|
|
|
|
|
|
{
|
|
|
|
|
|
trans.name = prefix + trans.name;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
foreach (var root in PostProcessInstructor.rootsToCombine)
|
2025-04-07 14:18:07 +08:00
|
|
|
|
{
|
2025-04-18 14:04:30 +08:00
|
|
|
|
if (!root)
|
2025-04-07 14:18:07 +08:00
|
|
|
|
{
|
2025-04-18 14:04:30 +08:00
|
|
|
|
continue;
|
2025-04-07 14:18:07 +08:00
|
|
|
|
}
|
2025-04-18 14:04:30 +08:00
|
|
|
|
|
|
|
|
|
|
Object.DestroyImmediate(root.gameObject);
|
2025-04-07 14:18:07 +08:00
|
|
|
|
}
|
2025-04-01 16:59:45 +08:00
|
|
|
|
}
|
2025-04-07 14:18:07 +08:00
|
|
|
|
catch (System.Exception ex)
|
2025-04-01 16:59:45 +08:00
|
|
|
|
{
|
2025-04-07 14:18:07 +08:00
|
|
|
|
Debug.LogError($"合批过程中发生错误: {ex.Message}\n{ex.StackTrace}");
|
|
|
|
|
|
// 如果合批失败,保留原始物体
|
|
|
|
|
|
if (combinedRoot != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Object.DestroyImmediate(combinedRoot);
|
|
|
|
|
|
}
|
2025-04-03 14:08:04 +08:00
|
|
|
|
}
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2025-05-15 19:27:10 +08:00
|
|
|
|
return combinedRoot;
|
2025-04-07 14:18:07 +08:00
|
|
|
|
}
|
2025-04-03 14:08:04 +08:00
|
|
|
|
|
2025-04-25 15:07:22 +08:00
|
|
|
|
#region 地面处理
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 修改地面贴图及材质的路径 用于打包
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ChangeMapTextureAndMatPath()
|
|
|
|
|
|
{
|
|
|
|
|
|
var ground = PostProcessInstructor.ground;
|
2025-04-25 15:07:22 +08:00
|
|
|
|
if (_unpackedCombinedObjectsRoot != null && HasClone)
|
2025-05-08 11:59:40 +08:00
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject;
|
2025-05-08 11:59:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-15 15:01:48 +08:00
|
|
|
|
GroundUtils.ChangeMapTextureAndMatPath(ground, ProcessedResourceDir, GetScenePrefix(),
|
2025-04-25 15:07:22 +08:00
|
|
|
|
groundRoot, CombinedObjectsRoot, _unpackedCombinedObjectsRoot);
|
2025-05-08 11:59:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:22:02 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 修改Terrain地形贴图及数据的路径 用于打包
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ChangeMapTextureAndMatPathForTerrain()
|
|
|
|
|
|
{
|
|
|
|
|
|
var terrain = PostProcessInstructor.terrain;
|
|
|
|
|
|
if (terrain == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("找不到Terrain对象,无法修改Terrain贴图和数据路径");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var terrainComponent = terrain.GetComponent<Terrain>();
|
|
|
|
|
|
if (terrainComponent == null || terrainComponent.terrainData == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Terrain对象缺少Terrain组件或TerrainData");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var originalTerrainData = terrainComponent.terrainData;
|
|
|
|
|
|
var originalPath = AssetDatabase.GetAssetPath(originalTerrainData);
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(originalPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("无法获取TerrainData的原始路径");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保目标目录存在
|
|
|
|
|
|
if (!AssetDatabase.IsValidFolder(ProcessedResourceDir))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"后处理目录不存在:{ProcessedResourceDir}");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 复制TerrainData到后处理文件夹
|
|
|
|
|
|
var newTerrainDataPath = $"{ProcessedResourceDir}/{originalTerrainData.name}.asset";
|
|
|
|
|
|
if (!AssetDatabase.CopyAsset(originalPath, newTerrainDataPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"复制TerrainData失败:{originalPath} -> {newTerrainDataPath}");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载新的TerrainData
|
|
|
|
|
|
var newTerrainData = AssetDatabase.LoadAssetAtPath<TerrainData>(newTerrainDataPath);
|
|
|
|
|
|
if (newTerrainData == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"加载新TerrainData失败:{newTerrainDataPath}");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理TerrainLayers及其贴图
|
|
|
|
|
|
var terrainLayers = newTerrainData.terrainLayers;
|
|
|
|
|
|
var newTerrainLayers = new TerrainLayer[terrainLayers.Length];
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < terrainLayers.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var originalLayer = terrainLayers[i];
|
|
|
|
|
|
if (originalLayer == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 为每个 TerrainLayer 创建新的实例并保存到后处理文件夹
|
|
|
|
|
|
var newLayer = Object.Instantiate(originalLayer);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理贴图并更新新layer的贴图引用为复制后的贴图
|
|
|
|
|
|
ProcessTerrainLayerTextures(newLayer);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新的TerrainLayer资产文件路径
|
|
|
|
|
|
var newLayerPath = $"{ProcessedResourceDir}/{originalLayer.name}_{i}.terrainlayer";
|
|
|
|
|
|
|
|
|
|
|
|
// 保存新的 TerrainLayer 为独立的资产文件
|
|
|
|
|
|
AssetDatabase.CreateAsset(newLayer, newLayerPath);
|
|
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
|
|
|
|
|
|
|
// 重新加载已保存的 TerrainLayer 确保引用正确
|
|
|
|
|
|
newTerrainLayers[i] = AssetDatabase.LoadAssetAtPath<TerrainLayer>(newLayerPath);
|
|
|
|
|
|
|
|
|
|
|
|
if (newTerrainLayers[i] == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"保存TerrainLayer失败:{newLayerPath}");
|
|
|
|
|
|
newTerrainLayers[i] = originalLayer; // 使用原始引用作为备选
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log($"成功创建新的TerrainLayer:{newLayerPath},使用复制后的贴图");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新TerrainData的TerrainLayers
|
|
|
|
|
|
newTerrainData.terrainLayers = newTerrainLayers;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新Terrain组件的TerrainData引用
|
|
|
|
|
|
terrainComponent.terrainData = newTerrainData;
|
|
|
|
|
|
|
|
|
|
|
|
// 同时更新TerrainCollider的TerrainData引用,避免不一致警告
|
|
|
|
|
|
var terrainCollider = terrain.GetComponent<TerrainCollider>();
|
|
|
|
|
|
if (terrainCollider != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
terrainCollider.terrainData = newTerrainData;
|
|
|
|
|
|
Debug.Log("已同步更新TerrainCollider的TerrainData引用");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 标记场景为脏,确保更改被保存
|
|
|
|
|
|
EditorSceneManager.MarkSceneDirty(terrain.scene);
|
|
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"Terrain贴图和数据路径修改完成:{newTerrainDataPath}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理TerrainLayer中的贴图
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ProcessTerrainLayerTextures(TerrainLayer terrainLayer)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (terrainLayer == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 处理漫反射贴图
|
|
|
|
|
|
if (terrainLayer.diffuseTexture != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
terrainLayer.diffuseTexture = CopyTextureToProcessedDir(terrainLayer.diffuseTexture);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理法线贴图
|
|
|
|
|
|
if (terrainLayer.normalMapTexture != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
terrainLayer.normalMapTexture = CopyTextureToProcessedDir(terrainLayer.normalMapTexture);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理遮罩贴图
|
|
|
|
|
|
if (terrainLayer.maskMapTexture != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
terrainLayer.maskMapTexture = CopyTextureToProcessedDir(terrainLayer.maskMapTexture);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 复制贴图到后处理目录
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Texture2D CopyTextureToProcessedDir(Texture2D originalTexture)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (originalTexture == null) return null;
|
|
|
|
|
|
|
|
|
|
|
|
var originalPath = AssetDatabase.GetAssetPath(originalTexture);
|
|
|
|
|
|
if (string.IsNullOrEmpty(originalPath)) return originalTexture;
|
|
|
|
|
|
|
|
|
|
|
|
var extension = System.IO.Path.GetExtension(originalPath);
|
|
|
|
|
|
var newTexturePath = $"{ProcessedResourceDir}/{originalTexture.name}{extension}";
|
|
|
|
|
|
|
|
|
|
|
|
// 如果文件已存在,直接加载返回
|
|
|
|
|
|
if (AssetDatabase.LoadAssetAtPath<Texture2D>(newTexturePath) != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return AssetDatabase.LoadAssetAtPath<Texture2D>(newTexturePath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 复制贴图文件
|
|
|
|
|
|
if (AssetDatabase.CopyAsset(originalPath, newTexturePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
|
var newTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(newTexturePath);
|
|
|
|
|
|
if (newTexture != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log($"复制贴图成功:{originalPath} -> {newTexturePath}");
|
|
|
|
|
|
return newTexture;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.LogWarning($"复制贴图失败:{originalPath} -> {newTexturePath}");
|
|
|
|
|
|
return originalTexture; // 返回原始贴图作为备选
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 给地面添加BoxCollider
|
|
|
|
|
|
/// </summary>
|
2025-04-18 16:33:53 +08:00
|
|
|
|
private void GroundAddBoxCollider()
|
|
|
|
|
|
{
|
|
|
|
|
|
var ground = PostProcessInstructor.ground;
|
|
|
|
|
|
if (_unpackedCombinedObjectsRoot != null && HasClone)
|
|
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject;
|
2025-04-18 16:33:53 +08:00
|
|
|
|
}
|
2025-11-27 17:03:33 +08:00
|
|
|
|
|
|
|
|
|
|
SceneUtils.RemoveCollider(ground);
|
|
|
|
|
|
var boxColliderObj = new GameObject("BoxCollider");
|
|
|
|
|
|
boxColliderObj.layer = LayerMask.NameToLayer(Framework.Constants.Layer.GROUND_NAME);
|
|
|
|
|
|
SceneUtils.AddBoxCollider(boxColliderObj);
|
2025-12-03 12:33:03 +08:00
|
|
|
|
boxColliderObj.transform.localScale = new Vector3(300, 300, 1f);
|
2025-11-27 17:03:33 +08:00
|
|
|
|
boxColliderObj.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
|
|
|
|
|
|
boxColliderObj.transform.SetParent(PostProcessInstructor.combinedObjectsRoot.transform, false);
|
|
|
|
|
|
|
2025-06-20 15:12:10 +08:00
|
|
|
|
// 移除下层地面的碰撞体
|
|
|
|
|
|
var ground_2 = GameObject.Find("Ground_2");
|
2025-11-27 17:03:33 +08:00
|
|
|
|
SceneUtils.RemoveCollider(ground_2);
|
2025-04-18 16:33:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 切割地面
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
private void SplitGroundTexture()
|
|
|
|
|
|
{
|
|
|
|
|
|
var ground = PostProcessInstructor.ground;
|
|
|
|
|
|
if (ground == null)
|
|
|
|
|
|
return;
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-07-15 15:01:48 +08:00
|
|
|
|
GroundUtils.SplitGroundTexture(ground, ProcessedResourceDir, GetScenePrefix(), GROUND_SPLIT_WIDTH,
|
2025-06-16 17:25:12 +08:00
|
|
|
|
GROUND_SPLIT_HEIGHT);
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-08 14:17:55 +08:00
|
|
|
|
#endregion
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置雾的位置
|
|
|
|
|
|
/// </summary>
|
2024-01-15 20:25:49 +08:00
|
|
|
|
private void SetFogPosition()
|
|
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
SceneUtils.SetFogPosition(PostProcessInstructor.fog);
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|
2024-01-16 14:23:05 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 销毁多余eventSystems
|
|
|
|
|
|
/// </summary>
|
2024-10-25 15:30:02 +08:00
|
|
|
|
private void RemoveRedundant()
|
|
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
SceneUtils.RemoveRedundantEventSystems(PostProcessInstructor.gameObject.scene);
|
2024-10-25 15:30:02 +08:00
|
|
|
|
}
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2024-01-16 14:23:05 +08:00
|
|
|
|
private void DestroyUselessObject()
|
|
|
|
|
|
{
|
2024-01-17 13:44:26 +08:00
|
|
|
|
foreach (var root in PostProcessInstructor.rootsToCombine)
|
2024-01-16 14:23:05 +08:00
|
|
|
|
{
|
2024-03-29 12:05:14 +08:00
|
|
|
|
if (!root)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2024-10-22 16:10:43 +08:00
|
|
|
|
|
2024-01-16 14:23:05 +08:00
|
|
|
|
Object.DestroyImmediate(root.gameObject, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object.DestroyImmediate(PostProcessInstructor.gameObject, true);
|
2025-06-20 15:12:10 +08:00
|
|
|
|
|
|
|
|
|
|
var designerObj = GameObject.Find("Designer");
|
|
|
|
|
|
if (designerObj != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Object.DestroyImmediate(designerObj, true);
|
|
|
|
|
|
}
|
2024-01-16 14:23:05 +08:00
|
|
|
|
}
|
2025-04-03 14:08:04 +08:00
|
|
|
|
|
2025-05-08 11:59:40 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检测是否为剧情场景
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-04-03 14:08:04 +08:00
|
|
|
|
private bool IsStoryScene()
|
|
|
|
|
|
{
|
2025-05-08 11:59:40 +08:00
|
|
|
|
var sceneName = PostProcessInstructor.gameObject.scene.name;
|
2025-04-03 14:08:04 +08:00
|
|
|
|
return sceneName.Contains(STORY_SCENE_PREFIX);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetScenePrefix()
|
|
|
|
|
|
{
|
|
|
|
|
|
return IsStoryScene() ? STORY_SCENE_PREFIX : "";
|
|
|
|
|
|
}
|
2025-04-07 18:55:27 +08:00
|
|
|
|
|
2025-04-08 14:17:55 +08:00
|
|
|
|
#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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-20 13:02:09 +08:00
|
|
|
|
private void HideCamera()
|
|
|
|
|
|
{
|
|
|
|
|
|
var scene = PostProcessInstructor.gameObject.scene;
|
|
|
|
|
|
var cameras = Object.FindObjectsOfType<Camera>();
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2025-05-20 13:02:09 +08:00
|
|
|
|
foreach (var camera in cameras)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (camera.gameObject.scene == scene)
|
|
|
|
|
|
{
|
|
|
|
|
|
camera.gameObject.SetActive(false);
|
|
|
|
|
|
// Debug.Log($"已隐藏相机: {camera.gameObject.name}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-16 17:25:12 +08:00
|
|
|
|
|
2025-05-20 18:34:26 +08:00
|
|
|
|
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
|
|
|
|
|
EditorSceneManager.SaveOpenScenes();
|
2025-05-20 13:02:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-08 14:17:55 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 烘焙
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 烘焙灯光贴图的方法
|
|
|
|
|
|
/// </summary>
|
2025-04-07 18:55:27 +08:00
|
|
|
|
private void BakeLightmaps()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否开启了烘焙选项
|
2025-04-16 15:28:21 +08:00
|
|
|
|
if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking)
|
2025-04-07 18:55:27 +08:00
|
|
|
|
return;
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-07 18:55:27 +08:00
|
|
|
|
Debug.Log("开始灯光烘焙过程...");
|
2025-04-18 16:33:53 +08:00
|
|
|
|
|
2025-04-17 19:25:12 +08:00
|
|
|
|
// 1. 从CombinedObjectsRoot下查找灯光 改为烘焙灯光
|
2025-04-25 15:07:22 +08:00
|
|
|
|
LightmapUtils.PrepareLight(CombinedObjectsRoot, realTimeLights, bakedLights);
|
2025-04-15 16:33:01 +08:00
|
|
|
|
|
2025-04-17 19:25:12 +08:00
|
|
|
|
// 2. 处理参与烘焙的节点
|
2025-06-16 17:25:12 +08:00
|
|
|
|
LightmapUtils.PreProcessBake(bakeReplaceShaderRoot, CombinedObjectsRoot, true, true, allBakeRenderers,
|
|
|
|
|
|
needReplaceRenderers);
|
|
|
|
|
|
LightmapUtils.PreProcessBake(groundRoot, CombinedObjectsRoot, false, false, allBakeRenderers,
|
|
|
|
|
|
needReplaceRenderers);
|
2025-04-17 19:25:12 +08:00
|
|
|
|
ProcessGround(3000);
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-17 19:25:12 +08:00
|
|
|
|
Debug.Log($"总共收集到 {needReplaceRenderers.Count} 个渲染器需要替换shader");
|
|
|
|
|
|
Debug.Log($"总共收集到 {allBakeRenderers.Count} 个渲染器参与烘焙");
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 3.替换Shader为支持光照贴图的Shader
|
2025-04-25 15:07:22 +08:00
|
|
|
|
MaterialUtils.ReplaceShaderWithLightmapShader(needReplaceRenderers);
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 4. 开始烘焙
|
2025-04-25 15:07:22 +08:00
|
|
|
|
LightmapUtils.Bake(SceneManager.GetActiveScene());
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 5. 关闭所有烘焙物体的阴影投射
|
2025-04-17 19:25:12 +08:00
|
|
|
|
foreach (var renderer in allBakeRenderers)
|
2025-04-07 18:55:27 +08:00
|
|
|
|
{
|
2025-04-15 16:33:01 +08:00
|
|
|
|
if (renderer != null)
|
2025-04-16 15:28:21 +08:00
|
|
|
|
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
2025-04-15 16:33:01 +08:00
|
|
|
|
}
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 6. 打开实时灯光
|
2025-04-25 15:07:22 +08:00
|
|
|
|
LightmapUtils.RevertLight(realTimeLights, bakedLights);
|
2025-04-16 15:28:21 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 7.还原地面RenderQueue
|
2025-04-17 19:25:12 +08:00
|
|
|
|
ProcessGround(2000);
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 8. 复制光照烘焙文件到目标路径
|
2025-04-25 15:07:22 +08:00
|
|
|
|
LightmapUtils.CopyLightmapFiles(SceneManager.GetActiveScene(), LightBakeDir);
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
// 9. 保存场景
|
2025-04-25 15:07:22 +08:00
|
|
|
|
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
2025-04-07 18:55:27 +08:00
|
|
|
|
EditorSceneManager.SaveOpenScenes();
|
2025-04-08 14:17:55 +08:00
|
|
|
|
|
2025-04-07 18:55:27 +08:00
|
|
|
|
Debug.Log("灯光烘焙处理完成");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 16:22:02 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 专门为Terrain地形烘焙灯光贴图的方法
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void BakeLightmapsForTerrain()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否开启了烘焙选项
|
|
|
|
|
|
if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("开始Terrain地形灯光烘焙过程...");
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 从CombinedObjectsRoot下查找灯光 改为烘焙灯光
|
|
|
|
|
|
LightmapUtils.PrepareLight(CombinedObjectsRoot, realTimeLights, bakedLights);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 处理参与烘焙的合批节点(不包括地面)
|
|
|
|
|
|
LightmapUtils.PreProcessBake(bakeReplaceShaderRoot, CombinedObjectsRoot, true, true, allBakeRenderers,
|
|
|
|
|
|
needReplaceRenderers);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 处理Terrain组件的烘焙设置
|
|
|
|
|
|
ProcessTerrainForBaking();
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"总共收集到 {needReplaceRenderers.Count} 个渲染器需要替换shader");
|
|
|
|
|
|
Debug.Log($"总共收集到 {allBakeRenderers.Count} 个渲染器参与烘焙");
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 替换Shader为支持光照贴图的Shader(不包括Terrain,Terrain使用内置材质)
|
|
|
|
|
|
MaterialUtils.ReplaceShaderWithLightmapShader(needReplaceRenderers);
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 开始烘焙
|
|
|
|
|
|
LightmapUtils.Bake(SceneManager.GetActiveScene());
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 关闭所有烘焙物体的阴影投射
|
|
|
|
|
|
foreach (var renderer in allBakeRenderers)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (renderer != null)
|
|
|
|
|
|
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 打开实时灯光
|
|
|
|
|
|
LightmapUtils.RevertLight(realTimeLights, bakedLights);
|
|
|
|
|
|
|
|
|
|
|
|
// 8. 复制光照烘焙文件到目标路径
|
|
|
|
|
|
LightmapUtils.CopyLightmapFiles(SceneManager.GetActiveScene(), LightBakeDir);
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 保存场景
|
|
|
|
|
|
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
|
|
|
|
|
|
EditorSceneManager.SaveOpenScenes();
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("Terrain地形灯光烘焙处理完成");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理Terrain组件的烘焙设置
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ProcessTerrainForBaking()
|
|
|
|
|
|
{
|
|
|
|
|
|
var terrain = PostProcessInstructor.terrain;
|
|
|
|
|
|
if (terrain == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("未找到Terrain对象,跳过Terrain烘焙设置");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var terrainComponent = terrain.GetComponent<Terrain>();
|
|
|
|
|
|
if (terrainComponent == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("Terrain对象缺少Terrain组件,跳过Terrain烘焙设置");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置Terrain的Static标志,使其参与烘焙
|
2026-01-13 13:20:29 +08:00
|
|
|
|
UnityEditorGameObjectUtility.SetStaticEditorFlags(terrain, StaticEditorFlags.ContributeGI);
|
2025-08-29 16:22:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取Terrain的MeshRenderer(如果有的话)
|
|
|
|
|
|
var terrainRenderer = terrain.GetComponent<MeshRenderer>();
|
|
|
|
|
|
if (terrainRenderer != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Terrain通常不使用MeshRenderer,但如果有的话也要处理
|
|
|
|
|
|
terrainRenderer.shadowCastingMode = ShadowCastingMode.On;
|
|
|
|
|
|
terrainRenderer.receiveShadows = true;
|
|
|
|
|
|
allBakeRenderers.Add(terrainRenderer);
|
|
|
|
|
|
Debug.Log("已将Terrain的MeshRenderer添加到烘焙列表");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置Terrain的烘焙相关属性
|
|
|
|
|
|
terrainComponent.shadowCastingMode = ShadowCastingMode.On;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保TerrainData支持光照贴图
|
|
|
|
|
|
if (terrainComponent.terrainData != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Terrain的光照贴图设置通过TerrainData处理
|
|
|
|
|
|
Debug.Log($"Terrain {terrain.name} 已设置为参与光照烘焙");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("Terrain烘焙设置完成");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-17 19:25:12 +08:00
|
|
|
|
private void ProcessGround(int renderQueue)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (PostProcessInstructor.ground != null)
|
|
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
SceneUtils.SetGroundRenderQueue(PostProcessInstructor.ground, renderQueue);
|
2025-04-17 19:25:12 +08:00
|
|
|
|
}
|
2025-04-16 15:28:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-18 14:04:30 +08:00
|
|
|
|
private void UnpackPostSceneArt()
|
|
|
|
|
|
{
|
2025-04-25 15:07:22 +08:00
|
|
|
|
if (CombinedObjectsRoot != null && PrefabUtility.IsPartOfPrefabInstance(CombinedObjectsRoot.gameObject))
|
2025-04-18 14:04:30 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 保存对象的引用
|
|
|
|
|
|
_unpackedCombinedObjectsRoot = CombinedObjectsRoot;
|
2025-08-13 13:00:36 +08:00
|
|
|
|
PhxhSDK.EditorUtils.UnpackPrefab(CombinedObjectsRoot.gameObject);
|
2025-04-18 14:04:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-08 14:17:55 +08:00
|
|
|
|
#endregion
|
2025-07-15 19:36:35 +08:00
|
|
|
|
|
|
|
|
|
|
#region 剧情Timeline
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-07-15 19:36:35 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置剧情timeline
|
|
|
|
|
|
/// </summary>
|
2025-08-07 16:19:51 +08:00
|
|
|
|
private async UniTask SetTimeline()
|
2025-07-15 19:36:35 +08:00
|
|
|
|
{
|
2025-08-07 16:19:51 +08:00
|
|
|
|
GameObject goOld = PostProcessInstructor.timeline;
|
|
|
|
|
|
if (null == goOld)
|
2025-07-15 19:36:35 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
2025-08-07 16:19:51 +08:00
|
|
|
|
PlayableDirector oldDirector = goOld.GetComponent<PlayableDirector>();
|
|
|
|
|
|
if (null == oldDirector)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("获取PlayableDirector失败");
|
2025-07-15 19:36:35 +08:00
|
|
|
|
return;
|
2025-08-07 16:19:51 +08:00
|
|
|
|
}
|
2025-07-15 19:36:35 +08:00
|
|
|
|
|
2025-08-07 16:19:51 +08:00
|
|
|
|
if (null == oldDirector.playableAsset)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("获取PlayableAsset失败");
|
2025-07-15 19:36:35 +08:00
|
|
|
|
return;
|
2025-08-07 16:19:51 +08:00
|
|
|
|
}
|
2025-07-15 19:36:35 +08:00
|
|
|
|
|
2025-08-07 16:19:51 +08:00
|
|
|
|
string oldPath = AssetDatabase.GetAssetPath(oldDirector.playableAsset);
|
|
|
|
|
|
string newPath = $"{ProcessedResourceDir}/{oldDirector.playableAsset.name}.playable";
|
|
|
|
|
|
if (!AssetDatabase.CopyAsset(oldPath, newPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Timeline拷贝失败");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-07-15 19:36:35 +08:00
|
|
|
|
|
2025-08-07 16:35:44 +08:00
|
|
|
|
GameObject goNew = new(goOld.name);
|
2025-08-07 16:19:51 +08:00
|
|
|
|
goNew.transform.parent = goOld.transform.parent;
|
|
|
|
|
|
goNew.transform.position = Vector3.zero;
|
|
|
|
|
|
PlayableDirector newDirector = goNew.AddComponent<PlayableDirector>();
|
|
|
|
|
|
newDirector.timeUpdateMode = oldDirector.timeUpdateMode;
|
|
|
|
|
|
newDirector.playOnAwake = oldDirector.playOnAwake;
|
|
|
|
|
|
newDirector.extrapolationMode = oldDirector.extrapolationMode;
|
|
|
|
|
|
newDirector.initialTime = oldDirector.initialTime;
|
|
|
|
|
|
|
|
|
|
|
|
// 新预制体绑定新Playable资源
|
|
|
|
|
|
newDirector.playableAsset = AssetDatabase.LoadAssetAtPath<TimelineAsset>(newPath);
|
|
|
|
|
|
newDirector.playableAsset.name = System.IO.Path.GetFileNameWithoutExtension(newPath);
|
|
|
|
|
|
SerializedObject directorSO = new(oldDirector);
|
|
|
|
|
|
SerializedProperty sceneBindings = directorSO.FindProperty("m_SceneBindings");
|
|
|
|
|
|
|
|
|
|
|
|
newDirector.Play();
|
|
|
|
|
|
await UniTask.NextFrame();
|
|
|
|
|
|
|
|
|
|
|
|
// 删除PlayableDirector.Bindgins的空Key项,并把原Timeline文件的轨道Key替换到当前Timeline文件的轨道对象
|
|
|
|
|
|
Dictionary<Object, Object> bindingsKV = new();
|
|
|
|
|
|
for (int i = 0; i < sceneBindings.arraySize; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
SerializedProperty bindingElement = sceneBindings.GetArrayElementAtIndex(i);
|
|
|
|
|
|
// key是源文件的轨道对象,value是当前文件的绑定value
|
|
|
|
|
|
Object key = bindingElement.FindPropertyRelative("key").objectReferenceValue;
|
|
|
|
|
|
Object value = bindingElement.FindPropertyRelative("value").objectReferenceValue;
|
2025-08-01 17:40:16 +08:00
|
|
|
|
|
2025-08-07 16:19:51 +08:00
|
|
|
|
if (null == key)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
bindingsKV.Add(key, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除无用绑定
|
|
|
|
|
|
while (sceneBindings.arraySize > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
sceneBindings.DeleteArrayElementAtIndex(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
directorSO.ApplyModifiedProperties();
|
|
|
|
|
|
|
|
|
|
|
|
// 设置有效绑定(新老timeline资源outputs顺序完全一致)
|
|
|
|
|
|
List<PlayableBinding> listOldBinding = oldDirector.playableAsset.outputs.ToList();
|
|
|
|
|
|
List<PlayableBinding> listNewBinding = newDirector.playableAsset.outputs.ToList();
|
|
|
|
|
|
for (int i = 0; i < listOldBinding.Count; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
TrackAsset oldTrackAsset = listOldBinding[i].sourceObject as TrackAsset;
|
|
|
|
|
|
TrackAsset newTrackAsset = listNewBinding[i].sourceObject as TrackAsset;
|
|
|
|
|
|
|
|
|
|
|
|
if (bindingsKV.TryGetValue(oldTrackAsset, out Object value))
|
|
|
|
|
|
newDirector.SetGenericBinding(newTrackAsset, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GameObject.DestroyImmediate(PostProcessInstructor.timeline);
|
|
|
|
|
|
AssetDatabase.SaveAssetIfDirty(newDirector);
|
|
|
|
|
|
}
|
2025-07-15 19:36:35 +08:00
|
|
|
|
#endregion
|
2024-01-15 20:25:49 +08:00
|
|
|
|
}
|