提交 60e20475 编写于 作者: 魔术师Dix's avatar 魔术师Dix

【DotsRender】更新一版渲染的配置生成;

上级 0f8f1ac3
......@@ -5,6 +5,7 @@
"Aster.Common",
"Aster.AsterTask",
"Aster.ECS",
"Aster.EditorUtils",
"Unity.Entities",
"Unity.Mathematics",
"Unity.Collections",
......
/*
*Copyright(C) 2024 by Chief All rights reserved.
*Unity版本:2023.2.5f1c1
*作者:Chief
*创建日期: 2024-03-07
*模块说明:Ecs通用模块-渲染部分编辑器工具
*版本: 1.0
*/
using System.Collections.Concurrent;
using System.Collections.Generic;
using UnityEngine;
namespace Aster.DotsRenderEditor
{
/// <summary>
/// 预制统计辅助
/// </summary>
public class PrefabStatisticsHelper
{
public PrefabInfo entity;
private Material material => entity.SharedMaterial;
private Mesh mesh => entity.SharedMesh;
private LODGroup lodGroup => entity.lodGroup;
private bool enableLodGroupCheck => lodGroup != null;
private bool IsLod0Mesh => entity.IsLod0Mesh;
private int SubMeshCount => entity.SubMeshCount;
public ConcurrentDictionary<string, Mesh> m_DictMeshes;
public ConcurrentDictionary<string, Material> m_DictMaterials;
public ConcurrentDictionary<string, PrefabStatisticsInfo> DictRenderInfoStatistics;
public ConcurrentQueue<string> ErrorStr;
public PrefabStatisticsParam param;
private List<PrefabStatisticsInfo> ListRenderInfoStatistics => param.ListRenderInfoStatistics;
private List<PrefabStatisticsErrorInfo> ListErrorPrefab => param.ListErrorPrefab;
private List<LODGroup> ListLodGroups => param.ListLodGroups;
private LOD[] lods => entity.Lods;
public string Prefix_Mat;
public string Prefix_Mesh;
public bool CheckGameObjectLayer()
{
int layer = 1 << entity.Layer;
//需要排除的层级
if (param.ExcludeLayer > 0)
{
if ((layer & param.ExcludeLayer) == layer)
return false;
}
//错误的层级
if (param.ErrorLayer > 0)
{
if ((layer & param.ErrorLayer) == layer)
{
var message = $"{entity.HierarchyName} 出错:不能是层级:{entity.Layer}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
}
return true;
}
public bool CheckLodGroup()
{
if (lodGroup == null)
return true;
//有 Lod 的情况,统一按照Lod0进行处理
var lod_0 = lods[0];
var lod0Renders = lod_0.renderers;
if (lod0Renders == null || lod0Renders.Length == 0)
{
var message = $"{entity.HierarchyName} 出错:Lod Group 配置丢失!";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
var r = lod0Renders[0] as MeshRenderer;
if (r == null)
{
var message = $"{entity.HierarchyName} 出错:没有 Lod 0 配置";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
for (int i = 0; i < lods.Length; i++)
{
var lod = lods[i];
if (lod.screenRelativeTransitionHeight <= 0)
{
var message = $"{entity.HierarchyName} 出错:有不合理LOD_{i}距离:{lod.screenRelativeTransitionHeight}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
continue;
}
}
return true;
}
public bool CheckMesh()
{
if (mesh == null)
{
var message = $"{entity.HierarchyName} 出错:没有网格";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
//查找不支持GPU Instance 的类型:
if (SubMeshCount > 1)
{
var message = $"{entity.Name} 有多个网格 :{entity.SubMeshCount} ";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
return true;
}
public bool CheckMaterial()
{
if (material == null)
{
var message = $"{entity.HierarchyName} 出错:没有材质球";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
if (entity.SharedMaterialsCount > 1)
{
var message = $"{entity.Name} 有多个材质球 :{entity.SharedMaterialsCount}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
if (!entity.EnableInstancing)
{
var message = $"{entity.Name} 材质未开启GPU Instance :{entity.MaterialName}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
return true;
}
/// <summary>
/// /分析重名材质球和网格
/// </summary>
/// <returns></returns>
public bool CheckDuplicateMeshAndMat()
{
//分析重名网格
string meshName = entity.MeshName;
if (m_DictMeshes.TryGetValue(meshName, out Mesh existMesh))
{
if (existMesh != mesh)
{
var message = $"有重名网格:{meshName}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
}
else
m_DictMeshes[meshName] = mesh;
//分析重名材质球
string matName = entity.MaterialName;
if (m_DictMaterials.TryGetValue(matName, out Material existMaterial))
{
if (existMaterial != material)
{
var message = $"有重名材质球:{meshName}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
}
else
m_DictMaterials[matName] = material;
if (!string.IsNullOrEmpty(Prefix_Mat))
{
if (!matName.StartsWith(Prefix_Mat))
{
var message = $"材质球没有以{Prefix_Mat}开头:{matName}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
}
//网格和材质球不能重名
if (matName == meshName)
{
var message = $"{entity.Name} 材质球和网格重名 :{entity.MaterialName} | {entity.MeshName}";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
return true;
}
public bool CheckTransform()
{
var scale = entity.Scale;
if (math.any(scale < 0.01f) || math.any(scale > 655.35f))
{
var message = $"{entity.HierarchyName} 缩放 {scale} 不符规范,需要在 0.01 ~ 655.35 之间!";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
return true;
}
/// <summary>
/// 设置统计信息
/// </summary>
/// <param name="checkSameTransform">是否检测有重复摆放的情况</param>
/// <returns></returns>
public bool SetStaticsInfo(bool checkSameTransform = true)
{
string uniqueKey = LocalUtils.GetUniqueKey(entity.MaterialName, entity.MeshName);
//统计渲染组合的信息结果
PrefabStatisticsInfo info;
if (!DictRenderInfoStatistics.TryGetValue(uniqueKey, out info))
{
lock (DictRenderInfoStatistics)
{
if (!DictRenderInfoStatistics.TryGetValue(uniqueKey, out info))
{
info = new PrefabStatisticsInfo()
{
material = material,
mesh = mesh,
UniqueKey = uniqueKey,
Trans = entity.transform
};
DictRenderInfoStatistics[uniqueKey] = info;
ListRenderInfoStatistics.Add(info);
if (enableLodGroupCheck && lodGroup != null && IsLod0Mesh)
ListLodGroups?.Add(lodGroup);
}
}
}
//不能重复摆放;
if (checkSameTransform && info.HasSameTransform(entity))
{
var message = $"{entity.HierarchyName} 重复摆放 :{entity.Position} !";
ErrorStr.Enqueue(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(entity.gameObject, message, entity.HierarchyName));
return false;
}
lock (info)
{
info.AddRefrenceTarget(entity);
}
return true;
}
}
}
fileFormatVersion: 2
guid: d79b9783f007ca54694d5f1119224373
\ No newline at end of file
......@@ -7,14 +7,352 @@
*版本: 1.0
*/
using Aster.AsterTask;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Unity.Mathematics;
using UnityEngine;
using Debug = Aster.Logger;
namespace Aster.DotsRenderEditor
{
/// <summary>
/// 编辑器工具
/// </summary>
public static class DotsRenderEditorUtils
public static class DotsRenderEditorUtils
{
#region 预制统计部分
public static int StatisticsMeshAndMaterial(PrefabStatisticsParam param)
{
var ListRenderInfoStatistics = param.ListRenderInfoStatistics;
var ListErrorPrefab = param.ListErrorPrefab;
var listLodGroup = param.ListLodGroups;
var transform = param.transform;
var rootPosition = param.RootPosition;
ListRenderInfoStatistics.Clear();
ListErrorPrefab.Clear();
bool enableLodGroupCheck = listLodGroup != null;
listLodGroup?.Clear();
var entities = new List<PrefabInfo>();
var rendereres = transform.GetComponentsInChildren<MeshRenderer>();
var terrains = transform.GetComponentsInChildren<Terrain>();
//用于重名分析的字典;
ConcurrentDictionary<string, Mesh> m_DictMeshes = new ConcurrentDictionary<string, Mesh>();
ConcurrentDictionary<string, Material> m_DictMaterials = new ConcurrentDictionary<string, Material>();
ConcurrentDictionary<string, PrefabStatisticsInfo> DictRenderInfoStatistics = new ConcurrentDictionary<string, PrefabStatisticsInfo>();
#region 对预设部件进行收集
// 优先预设部件数据
if (param.PartGlobalSetting != null && param.PartGlobalSetting.ListPresetPart != null)
{
var L = param.PartGlobalSetting.ListPresetPart;
var tempRenderList = new List<MeshRenderer>(3);
for (int i = 0; i < L.Count; i++)
{
var obj = L[i];
if (obj == null)
continue;
EditorUtils.DisplayProgress("预制统计", $"预设部件:{obj}", i, L.Count);
obj.GetComponentsInChildren(tempRenderList);
foreach (var render in tempRenderList)
{
if (render == null)
continue;
var pInfo = new PrefabInfo();
pInfo.SetRender(render);
pInfo.Position = render.transform.position;
pInfo.Rotation = render.transform.rotation;
pInfo.Scale = render.transform.lossyScale;
entities.Add(pInfo);
}
}
}
for (int i = 0; i < rendereres.Length; i++)
{
var render = rendereres[i];
EditorUtils.DisplayProgress("预制统计", $"收集渲染器:{render.gameObject.name}", i, rendereres.Length);
var noScaleLocalPos = render.transform.position - rootPosition;
var pInfo = new PrefabInfo();
pInfo.SetRender(render);
pInfo.Position = noScaleLocalPos;
pInfo.Rotation = render.transform.rotation;
pInfo.Scale = render.transform.lossyScale;
entities.Add(pInfo);
}
int totalTreeCount = 0;
for (int i = 0; i < terrains.Length; i++)
{
var terrain = terrains[i];
totalTreeCount += terrain.terrainData.treeInstanceCount;
}
int curTreeIndex = 0;
for (int i = 0; i < terrains.Length; i++)
{
var terrain = terrains[i];
var trees = terrain.terrainData.treeInstances;
var protoTypes = terrain.terrainData.treePrototypes;
for (int j = 0; j < trees.Length; j++, curTreeIndex++)
{
EditorUtils.DisplayProgress("预制统计", $"收集地形预设:{terrain.gameObject.name}", curTreeIndex, totalTreeCount);
var tree = trees[j];
var treeId = tree.prototypeIndex;
var type = protoTypes[treeId];
if (type.prefab == null)
{
Debug.LogError($"{terrains[i].name} 缺失树预制体 编号 {treeId}");
continue;
}
var trans = type.prefab.transform;
var lodGroup = trans.GetComponent<LODGroup>();
if (lodGroup != null)
{
var lods = lodGroup.GetLODs();
//这里需要把所有LODGroup的设置都丢进去;
foreach (var subLod in lods)
{
if (subLod.renderers.Length <= 0 || subLod.renderers[0] == null)
{
var message = $"地形树: {type.prefab} 出错:Lod Group 配置异常!!";
Debug.LogError(message);
ListErrorPrefab.Add(new PrefabStatisticsErrorInfo(type.prefab, message, type.prefab.transform.GetHierarchyName()));
continue;
}
var r = subLod.renderers[0] as MeshRenderer;
if (r != null)
{
var pInfo = CreatePrefabInfo(r, tree, terrain, rootPosition);
entities.Add(pInfo);
}
}
continue;
}
var render = trans.GetComponent<MeshRenderer>();
if (render != null)
{
var pInfo = CreatePrefabInfo(render, tree, terrain, rootPosition);
entities.Add(pInfo);
}
}
}
#endregion
#region 检查所有的预设部件
int TotalRenderCount = entities.Count;
bool isParallelFinish = false;
string curEntityName = null;
CountdownEvent countdownEvent = new CountdownEvent(TotalRenderCount);
ConcurrentQueue<string> errorStr = new ConcurrentQueue<string>();
var task = TaskUtils.Run(() =>
{
Parallel.ForEach(entities, entity =>
{
try
{
curEntityName = entity.Name;
if (entity.Renderer != null)
{
var helper = new PrefabStatisticsHelper()
{
entity = entity,
param = param,
m_DictMeshes = m_DictMeshes,
m_DictMaterials = m_DictMaterials,
DictRenderInfoStatistics = DictRenderInfoStatistics,
ErrorStr = errorStr
};
helper.StatisticsInParallel();
}
countdownEvent.Signal();
}
catch (System.Exception ex)
{
Debug.LogError($"预制统计:{curEntityName} 出错:{ex.Message}\n{entity.HierarchyName}");
Debug.LogError(ex.StackTrace);
}
});
isParallelFinish = true;
});
#endregion
var stopwatch = Stopwatch.StartNew();
while (!isParallelFinish)
{
long usedTime = stopwatch.ElapsedMilliseconds;
float curIndex = countdownEvent.InitialCount - countdownEvent.CurrentCount;
float progress = curIndex / countdownEvent.InitialCount;
//计算剩余时间;
float leftTime = usedTime * (1 - progress) / progress / 1000.0f;
string leftTimeStr = $"{(int)leftTime / 60}{(int)leftTime % 60}秒";
EditorUtils.DisplayProgressBar($"正在统计预制({progress * 100:f2}%)->预计剩余时间:[{leftTimeStr}]", $"【{curEntityName}】:{curIndex}/{TotalRenderCount}", progress);
}
stopwatch.Stop();
if (errorStr.Count > 0)
{
//打印错误;
TaskUtils.Run(async () =>
{
int errMsgCount = errorStr.Count;
Debug.LogError($"错误开始打印,总共:{errMsgCount}");
while (errorStr.Count > 0)
{
string msg;
if (errorStr.TryDequeue(out msg))
Debug.LogError(msg);
await Task.Delay(10);
}
Debug.LogError($"错误打印完成,总共:{errMsgCount}");
});
}
Debug.Log($"所有预制统计完成 --> [{isParallelFinish},{task.IsCompleted},{task.IsCompletedSuccessfully},{task.IsFaulted}]!");
EditorUtils.ClearProgressBar();
return TotalRenderCount;
}
private static void StatisticsInParallel(this PrefabStatisticsHelper helper)
{
if (!helper.CheckGameObjectLayer())
return;
if (!helper.CheckLodGroup())
return;
if (!helper.CheckMesh())
return;
if (!helper.CheckMaterial())
return;
if (!helper.CheckDuplicateMeshAndMat())
return;
if (!helper.CheckTransform())
return;
if (!helper.SetStaticsInfo(false))
return;
}
private static PrefabInfo CreatePrefabInfo(MeshRenderer renderer, TreeInstance tree, Terrain terrain, Vector3 rootPos)
{
var pInfo = new PrefabInfo();
var tPos = terrain.GetPosition();
var rot = tree.rotation * Mathf.Rad2Deg;
pInfo.SetRender(renderer);
pInfo.Position = Vector3.Scale(tree.position, terrain.terrainData.size) + tPos - rootPos;
pInfo.Rotation = Quaternion.Euler(0, rot, 0);
pInfo.Scale = new Vector3(tree.widthScale, tree.heightScale, tree.widthScale);
return pInfo;
}
public static int4 GetLodID(this PartLodConfig config, List<PartConfig_Local> partList)
{
int x = partList.GetPartID(config.Lod_0, false);
int y = partList.GetPartID(config.Lod_1, true);
int z = partList.GetPartID(config.Lod_2, true);
int w = partList.GetPartID(config.Lod_3, true);
return new int4(x, y, z, w);
}
public static int GetPartID(this List<PartConfig_Local> partList, string uniqueKey, bool markInLod = false)
{
if (partList == null)
return -1;
if (string.IsNullOrEmpty(uniqueKey))
return -1;
var partConfig = partList.Find(x => x.Key == uniqueKey);
if (partConfig == null)
{
Debug.LogError($"当前列表中没有找到:{uniqueKey}");
return -1;
}
partConfig.IsInLod = markInLod;
return partConfig.PartID;
}
#endregion
#region 辅助方法
public static string GetUniqueKey(this MeshRenderer render)
{
if (render == null)
{
Debug.LogError("MesheRender 为空!");
return null;
}
var meshFliter = render.GetComponent<MeshFilter>();
if (meshFliter == null)
{
Debug.LogError($"{render.gameObject} MeshFilter 为空!");
return null;
}
var mesh = meshFliter.sharedMesh;
if (mesh == null)
{
Debug.LogError($"{render.gameObject} 没有设置网格!");
return null;
}
var mat = render.sharedMaterial;
if (mat == null)
{
Debug.LogError($"{render.gameObject} 没有设置材质球!");
return null;
}
return GetUniqueKey(mat, mesh);
}
public static string GetUniqueKey(Material mat, Mesh mesh)
{
return $"{mesh.name}_{mat.name}";
}
public static string GetUniqueKey(string mat, string mesh)
{
return $"{mesh}_{mat}";
}
#endregion
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册