NLDClient-yudde/ProjectNLD/Assets/Editor/Scene/PostProcesser/PostProcessor.cs

339 lines
13 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
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/AutoGen/PostProcessScenes";
protected const string PREFAB_POSTPROCESS_DIR = "Assets/Art/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<string> 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)
{
if (!CheckPostProcessInstructor(postProcessInstructor))
return;
Init(postProcessInstructor);
HasClone = false;
EditorSceneManager.SaveOpenScenes();
PrepareFolders();
//NLDSceneManager = CommonUtils.GetSceneRootComponent<NLDSceneManager>(Scene);
//暂时先不渲染成面片(地面阴影还没法解决,以及一些与笔刷工具的耦合)
//BakeSurfaceData();
_clonedProcessInstructor = Clone();
HasClone = true;
if (_clonedProcessInstructor == null)
{
Debug.LogError("_clonedProcessInstructor is null!");
return;
}
BakeCombinedGameObject();
SplitGroundTexture();
SetFogPosition();
DestroyUselessObject();
AfterAll(destroyAfterPostProcess);
PostProcessData.Instance.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<PostProcessInstructor>();
return instructors.FirstOrDefault(pi => pi.gameObject.scene == scene);
}
// private void DeleteSurfaceData(SceneSurfaceData surfaceData)
// {
// var mat = AssetDatabase.LoadAssetAtPath<Material>(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<SceneObjectRenderInfo> 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<Shader>(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<Renderer> CollectRenderers()
{
List<Renderer> allRenderers = new();
foreach (var root in PostProcessInstructor.rootsToCombine)
{
if (!root)
{
continue;
}
var render = root.GetComponentsInChildren<Renderer>();
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<MeshRenderer>();
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<Collider>());
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<MeshRenderer>().sharedMaterial = splitMat;
splitGround.GetComponent<MeshRenderer>().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<Renderer>());
Object.DestroyImmediate(groundParent.GetComponent<Collider>());
var boxCollider = groundParent.gameObject.AddComponent<BoxCollider>();
boxCollider.size = new Vector3(100, 100, 1);
boxCollider.center = new Vector3(0, 0, 0.5f);
Object.DestroyImmediate(groundParent.GetComponent<MeshFilter>());
}
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 DestroyUselessObject()
{
foreach (var root in PostProcessInstructor.rootsToCombine)
{
if (!root)
{
continue;
}
Object.DestroyImmediate(root.gameObject, true);
}
Object.DestroyImmediate(PostProcessInstructor.gameObject, true);
}
}