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
}
}