using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.SceneManagement; namespace Thousandto.Launcher.ExternalLibs { /// /// 遮挡处理 /// public class OcclusionCullingUtil { private OcMapDataScript _ocDataScript = null; private const string _rootName = "SceneRoot"; private GameObject _sceneRoot = null; private List _childs = new List(); private List _limitList = new List(); private List _colRowList = new List(); //视口List private List _portalList = new List(); private Dictionary> _dicMapCell = new Dictionary>(); //ocNode字典 private Dictionary _dicOcNode = new Dictionary(); //检查高度 private List _verticalSize = new List { 24f,25f, 26f, 27f, 28f, 30f}; //缓存手动添加了boxCollider的节点 private List _cacheAddBoxTransList = new List(); //地图的最小点和最大点 private Vector3 _min = Vector3.zero; private Vector3 _max = Vector3.zero; //测试 public List testLineList = new List(); public List textboundsList = new List(); public List ColRowList { get { return _colRowList; } } public Dictionary> DicMapCell { get { return _dicMapCell; } } public Vector3 Min { get { return _min; } } public Vector3 Max { get { return _max; } } public List VerticalSize { get { return _verticalSize; } } #region//私有函数 //初始化Root private void Init() { //初始化根节点 和所有render if (_sceneRoot == null) { _sceneRoot = GameObject.Find(_rootName); var debugScript = RequireComponent(_sceneRoot);//_sceneRoot.AddComponent(); debugScript.ocUtile = this; } _childs.Clear(); Renderer[] rds = _sceneRoot.GetComponentsInChildren(true); if (rds != null) { for (int i = 0; i < rds.Length; i++) { OcNode node = new OcNode(); node.trans = rds[i].transform; if (!node.trans.gameObject.activeSelf) continue; node.rd = rds[i]; _childs.Add(node); if (!_dicOcNode.ContainsKey(node.trans)) { _dicOcNode.Add(node.trans, node); //判断是否有boxCollider var collider = node.trans.GetComponent(); if (collider == null) { //添加boxCollider并且缓存 node.trans.gameObject.AddComponent(); _cacheAddBoxTransList.Add(node.trans); } } } } //初始化cell限制条件 RegionLimit size1 = new RegionLimit(OcDefine.OC_Big, 5, int.MaxValue); RegionLimit size2 = new RegionLimit(OcDefine.OC_Mid, 2, 5); RegionLimit size3 = new RegionLimit(OcDefine.OC_Small, 0, 2); _limitList.Add(size3); _limitList.Add(size2); _limitList.Add(size1); //初始化数据保存脚本 _ocDataScript = ScriptableObject.CreateInstance(); //初始化地图大小 Vector2 min = Vector2.zero; Vector2 max = Vector2.zero; SetMapMinMax(min, max); } private void Start() { Init(); //把地图按照 大中小 三个不同尺寸划分成3组 part数据 for (int i = 0; i < (int)OcDefine.Count; i++) { SetCell(_min, _max, (OcDefine)i); if (_dicMapCell.ContainsKey(i)) { List listCell = _dicMapCell[i]; CheckChild(listCell, (OcDefine)i); } } //把场景节点按照占位格子数划分到small mid big 三个类型的cell 中 for (int i = 0; i < (int)OcDefine.Count; i++) { for (int m = 0; m < _childs.Count; m++) { DifferentNode(_childs[m], (OcDefine)i); } } for (int i = 0; i < _childs.Count; i++) { _childs[i].SetRealRefrenceCellIds(); } //设置视口 SetPortal(_min,_max); CalculatePVS(); //清除缓存的collider for (int i = 0; i < _cacheAddBoxTransList.Count; i++) { Object.DestroyImmediate(_cacheAddBoxTransList[i].GetComponent()); } //设置数据脚本数据 List portalVisableList = new List(); var enumer = _ocDataScript.dicPortalVisable.GetEnumerator(); try { while (enumer.MoveNext()) { OcMapDataScript.PortalVisableData data = new OcMapDataScript.PortalVisableData(); data.portalId = enumer.Current.Key; data.pathList = enumer.Current.Value; portalVisableList.Add(data); } } finally { enumer.Dispose(); } _ocDataScript.visableArray = portalVisableList.ToArray(); _ocDataScript.colRow = _colRowList[(int)OcDefine.OC_Small]; #if UNITY_EDITOR //保存数据脚本资源 string savePath = "GameAssets/Resources/Scene/OcclusionCulling"; string floderPath = Application.dataPath + "/" + savePath; if (!Directory.Exists(floderPath)) Directory.CreateDirectory(floderPath); string sceneName = SceneManager.GetActiveScene().name; string assetName = string.Format("{0}_OcMapScript.asset", sceneName); savePath = string.Format("Assets/{0}/{1}", savePath, assetName); UnityEditor.AssetDatabase.CreateAsset(_ocDataScript, savePath); UnityEditor.AssetDatabase.SaveAssets(); //保存场景 UnityEditor.SceneManagement.EditorSceneManager.SaveScene(SceneManager.GetActiveScene()); #endif } private void SetMapMinMax(Vector3 min, Vector3 max) { bool isInitMinMax = false; for (int i = 0; i < _childs.Count; i++) { if (!_childs[i].trans.gameObject.activeSelf) continue; Vector3 childMin = Vector3.zero; Vector3 childMax = Vector3.zero; var rd = _childs[i].trans.GetComponent(); Mesh ms = null; if (_childs[i].rd is MeshRenderer) { MeshFilter mf = _childs[i].trans.GetComponent(); ms = mf.sharedMesh; _childs[i].rdType = OcRender.OC_MeshRender; } else if (_childs[i].rd is SkinnedMeshRenderer) { SkinnedMeshRenderer skinRender = _childs[i].trans.GetComponent(); ms = skinRender.sharedMesh; _childs[i].rdType = OcRender.OC_SkinRender; } else if (_childs[i].rd is ParticleSystemRenderer) { _childs[i].rdType = OcRender.OC_PsRender; } for (int j = 0; ms != null && j < ms.vertexCount; j++) { Vector3 worldVec = _childs[i].trans.TransformPoint(ms.vertices[j]); //获取最小坐标和最大坐标 if (!isInitMinMax) { min.x = worldVec.x; min.y = worldVec.z; max.x = worldVec.x; max.y = worldVec.z; isInitMinMax = true; } else { min.x = worldVec.x < min.x ? worldVec.x : min.x; min.y = worldVec.z < min.y ? worldVec.z : min.y; max.x = worldVec.x > max.x ? worldVec.x : max.x; max.y = worldVec.z > max.y ? worldVec.z : max.y; } if (j == 0) { childMin.x = worldVec.x; childMin.y = worldVec.z; childMax.x = worldVec.x; childMax.y = worldVec.z; } else { childMin.x = worldVec.x < childMin.x ? worldVec.x : childMin.x; childMin.y = worldVec.z < childMin.y ? worldVec.z : childMin.y; childMax.x = worldVec.x > childMax.x ? worldVec.x : childMax.x; childMax.y = worldVec.z > childMax.y ? worldVec.z : childMax.y; } } _childs[i].Min = childMin; _childs[i].Max = childMax; } _min = min; _max = max; } //设置视口 private void SetPortal(Vector2 min, Vector3 max) { //视口大小和最小格子尺寸一样 int colRow = 0; float celWide = 0; float wide = max.x - min.x; float high = max.y - min.y; OcCellWide ocWide = OcCellWide.OC_PortalWide; SetCol_Row(wide, wide, ocWide, ref colRow); celWide = wide / colRow; float celHigh = high / colRow; int count = (int)Mathf.Pow(colRow, 2); for (int i = 0; i < count; i++) { MapPortal portal = new MapPortal(i, colRow, celWide, celHigh, min); //设置视口上的射线起点 int pointCount = (int)ocWide * 2; portal.rayStartPointList = new List(pointCount); for (int k = 0; k < pointCount; k++) { Vector3 point = new Vector3(); float x = UnityEngine.Random.Range(0, celWide); float z = UnityEngine.Random.Range(0, celHigh); point.x = portal.min.x + x; point.z = portal.min.z + z; portal.rayStartPointList.Add(point); } _portalList.Add(portal); } } //设置cell public void SetCell(Vector2 min, Vector3 max, OcDefine type) { int colRow = 0; float celWide = 0; float wide = max.x - min.x; float high = max.y - min.y; OcCellWide ocWide = OcCellWide.OC_SmallWide; switch (type) { case OcDefine.OC_Small: ocWide = OcCellWide.OC_SmallWide; break; case OcDefine.OC_Mid: ocWide = OcCellWide.OC_MideWide; break; case OcDefine.OC_Big: ocWide = OcCellWide.OC_BigWide; break; } SetCol_Row(wide, wide, ocWide, ref colRow); celWide = wide / colRow; float celHigh = high / colRow; int count = (int)Mathf.Pow(colRow, 2); _colRowList.Add(colRow); List celList = new List(); for (int i = 0; i < count; i++) { MapCell cell = new MapCell(i, colRow, celWide, celHigh, min, type); //设置cell射线终点 int endPointCount = GetCellEndRayPointCount(type); cell.rayEndPointList = new List(endPointCount); for (int k = 0; k < endPointCount; k++) { Vector3 point = new Vector3(); float x = UnityEngine.Random.Range(0, celWide); float z = UnityEngine.Random.Range(0, celHigh); point.x = cell.min.x + x; point.z = cell.min.z + z; cell.rayEndPointList.Add(point); } celList.Add(cell); } int key = (int)type; if (!_dicMapCell.ContainsKey(key)) { _dicMapCell.Add(key, celList); } } //计算各个视口中显示的物件 private void CalculatePVS() { for (int i = 0; i < _portalList.Count; i++) { var enumer = _dicMapCell.GetEnumerator(); try { while(enumer.MoveNext()) { List list = enumer.Current.Value; if (list != null) { for (int k = 0; k < list.Count; k++) { CalculateCellPVS(list[k], _portalList[i]); } } } } finally { enumer.Dispose(); } } } //计算视口起始点和cell终点射线碰撞 private void CalculateCellPVS(MapCell cell, MapPortal portal) { for (int k = 0; k < portal.rayStartPointList.Count; k++) { Vector3 origin = portal.rayStartPointList[k]; List heightList = new List(); for (int i = 0; i < cell.rayEndPointList.Count; i++) { for (int j = 0; j < _verticalSize.Count; j++) { float height = _verticalSize[j]; Vector3 start = origin + Vector3.up * height; //-----如果是最后一层 检查和第一层之间的斜线(主要处理有凹陷的物件, 水平方向无法检测碰撞)---// heightList.Clear(); heightList.Add(_verticalSize[j]); if (j == _verticalSize.Count -1) { heightList.Add(_verticalSize[0]); } //-----如果是最后一层 检查和第一层之间的斜线(主要处理有凹陷的物件, 水平方向无法检测碰撞)---// for (int m = 0; m < heightList.Count; m++) { float endHeight = heightList[m]; Vector3 end = cell.rayEndPointList[i] + Vector3.up * endHeight; Vector3 direction = (end - start).normalized; float distance = Vector3.Distance(end, start); RaycastHit hitInfo; if (Physics.Raycast(start, direction, out hitInfo, distance)) { if (portal.id == 175 && cell.id == 176) { UnityEngine.Debug.LogError(string.Format("碰撞物件的名称是 : {0}", hitInfo.collider.transform)); OcDebugLine.TestLineDate data = new OcDebugLine.TestLineDate(); data.star = start; data.end = end; testLineList.Add(data); if (textboundsList.Count < 2) { Vector3 center = Vector3.zero; center.x = portal.max.x - (portal.max.x - portal.min.x) / 2; center.z = portal.max.z - (portal.max.z - portal.min.z) / 2; var size = new Vector3(portal.max.x - portal.min.x, 400, portal.max.z - portal.min.z); Bounds bounds = new Bounds(center, size); bounds.min = new Vector3(portal.min.x, -200, portal.min.z); bounds.max = new Vector3(portal.max.x, 200, portal.max.z); textboundsList.Add(bounds); textboundsList.Add(cell.bounds); } } if (distance > 30) continue; Transform trans = hitInfo.collider.transform; if (!_dicOcNode.ContainsKey(trans)) continue; OcNode node = _dicOcNode[trans]; if (node == null) continue; if (node.type != cell.type) continue; List list = node.GetRefrenceIds(cell.type); if (list == null) continue; if (!list.Contains(cell.id)) continue; if (portal.transList.Contains(trans)) continue; portal.transList.Add(trans); List nameList = null; string path = string.Empty; path = GetTransPath(trans, path); if (_ocDataScript.dicPortalVisable.ContainsKey(portal.id)) { nameList = _ocDataScript.dicPortalVisable[portal.id]; nameList.Add(path); } else { nameList = new List(); nameList.Add(path); _ocDataScript.dicPortalVisable.Add(portal.id, nameList); } } } } } } } //设置格子行列 (行列相等) private void SetCol_Row(float celWide, float wide, OcCellWide type, ref int colRow) { if (celWide > (int)type) { colRow += 1; celWide = wide / colRow; SetCol_Row(celWide, wide, type, ref colRow); } } private void CheckChild(List listCell, OcDefine type) { //判断child所属区块 for (int i = 0; i < _childs.Count; i++) { Bounds bounds = new Bounds(); switch (_childs[i].rdType) { case OcRender.OC_MeshRender: { Mesh ms = _childs[i].trans.GetComponent().sharedMesh; Transform trans = _childs[i].trans; if (trans == null || ms == null) break; bounds = GetBounds(_childs[i], ms); SetCommpentPart(listCell, type, _childs[i], bounds); } break; case OcRender.OC_SkinRender: { SkinnedMeshRenderer skinnedRender = _childs[i].trans.GetComponent(); if (_childs[i].trans == null || skinnedRender.sharedMesh == null) break; bounds = GetBounds(_childs[i], skinnedRender.sharedMesh); SetCommpentPart(listCell, type, _childs[i], bounds); } break; case OcRender.OC_PsRender: { ParticleSystemRenderer prd = _childs[i].trans.GetComponent(); SetCommpentPart(listCell, type, _childs[i]); } break; } } } private void SetCommpentPart(List listCell, OcDefine type, OcNode node, Bounds bounds) { for (int j = 0; j < listCell.Count; j++) { bool isLast = j == listCell.Count - 1 ? true : false; //判断是否相交 bool isIntersects = listCell[j].Intersects(bounds); if (isIntersects) { List refrenceList = new List(); if (node.dicRefrenceCellIds.ContainsKey((int)type)) { refrenceList = node.dicRefrenceCellIds[(int)type]; refrenceList.Add(j); } else { refrenceList = new List(); refrenceList.Add(j); node.dicRefrenceCellIds.Add((int)type, refrenceList); } } } } private void SetCommpentPart(List listCell, OcDefine type, OcNode node) { for (int j = 0; j < listCell.Count; j++) { bool isLast = j == listCell.Count - 1 ? true : false; //判断是否相交 Bounds bounds = GetParticleBounds(node); bool isIntersects = listCell[j].Intersects(bounds); if (isIntersects) { List refrenceList = new List(); if (node.dicRefrenceCellIds.ContainsKey((int)type)) { refrenceList = node.dicRefrenceCellIds[(int)type]; refrenceList.Add(j); } else { refrenceList = new List(); refrenceList.Add(j); node.dicRefrenceCellIds.Add((int)type, refrenceList); } } } } private void DifferentNode(OcNode node, OcDefine type) { int min = 0; int max = 0; switch (type) { case OcDefine.OC_Small: min = 0; max = (int)OcCellWide.OC_SmallWide; break; case OcDefine.OC_Mid: min = (int)OcCellWide.OC_SmallWide; max = (int)OcCellWide.OC_MideWide; break; case OcDefine.OC_Big: min = (int)OcCellWide.OC_MideWide; max = (int)OcCellWide.OC_BigWide; break; } float wide = (node.Max.x - node.Min.x)>= (node.Max.z - node.Min.z)? (node.Max.x - node.Min.x) : (node.Max.z - node.Min.z); if (wide > min && wide <= max) { node.type = type; node.realKey = (int)type; } } private Bounds GetBounds(OcNode node, Mesh ms) { var center = node.trans.localToWorldMatrix.MultiplyPoint3x4(ms.bounds.center); var extents = new Vector3(Mathf.Abs(center.x - node.Min.x), 10, Mathf.Abs(center.z - node.Min.y)); var childMin = center - extents; var childMax = center + extents; var bounds = new Bounds(center, extents * 2); bounds.min = childMin; bounds.max = childMax; return bounds; } private Bounds GetParticleBounds(OcNode node) { Bounds bounds; ParticleSystem ps = node.trans.GetComponent(); var center = node.trans.position; var extents = new Vector3(ps.shape.scale.x * node.trans.localScale.x / 2, 10, ps.shape.scale.y * node.trans.localScale.y / 2); var childMin = center - extents; var childMax = center + extents; bounds = new Bounds(center, extents * 2); bounds.min = childMin; bounds.max = childMax; return bounds; } //获取trans相对于SceneRoot的路径 private string GetTransPath(Transform trans, string path) { if (trans == null) return path; if(string.IsNullOrEmpty(path)) path = string.Format("{0}", trans.name); else path = string.Format("{0}/{1}", trans.name, path); if (string.Equals(trans.name, "SceneRoot")) return path; return GetTransPath(trans.parent, path); } //获取cell上射线结束点个数 private int GetCellEndRayPointCount(OcDefine type) { int endPointCount = 0; switch (type) { case OcDefine.OC_Small: endPointCount = (int)OcCellWide.OC_SmallWide; endPointCount = Random.Range(endPointCount, endPointCount * 2); break; case OcDefine.OC_Mid: endPointCount = (int)OcCellWide.OC_MideWide; endPointCount = Random.Range(endPointCount, endPointCount * 2); break; case OcDefine.OC_Big: endPointCount = (int)OcCellWide.OC_BigWide; endPointCount = Random.Range(endPointCount, endPointCount * 2); break; } return endPointCount; } //获取或者添加脚本 private T RequireComponent(GameObject go) where T : Component { T ret = go.GetComponent(); if (ret == null) { ret = go.AddComponent(); } return ret; } #endregion #region//公共函数 public static void StartBaker() { OcclusionCullingUtil oc = new OcclusionCullingUtil(); oc.Start(); } #endregion } }