339 lines
13 KiB
C#
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);
|
|
}
|
|
} |