Files
Main/Assets/Launcher/ExternalLibs/OcclusionCulling/OcclusionCullingUtil.cs
2025-01-25 04:38:09 +08:00

677 lines
27 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Thousandto.Launcher.ExternalLibs
{
/// <summary>
/// 遮挡处理
/// </summary>
public class OcclusionCullingUtil
{
private OcMapDataScript _ocDataScript = null;
private const string _rootName = "SceneRoot";
private GameObject _sceneRoot = null;
private List<OcNode> _childs = new List<OcNode>();
private List<RegionLimit> _limitList = new List<RegionLimit>();
private List<int> _colRowList = new List<int>();
//视口List
private List<MapPortal> _portalList = new List<MapPortal>();
private Dictionary<int, List<MapCell>> _dicMapCell = new Dictionary<int, List<MapCell>>();
//ocNode字典
private Dictionary<Transform, OcNode> _dicOcNode = new Dictionary<Transform, OcNode>();
//检查高度
private List<float> _verticalSize = new List<float> { 24f,25f, 26f, 27f, 28f, 30f};
//缓存手动添加了boxCollider的节点
private List<Transform> _cacheAddBoxTransList = new List<Transform>();
//地图的最小点和最大点
private Vector3 _min = Vector3.zero;
private Vector3 _max = Vector3.zero;
//测试
public List<OcDebugLine.TestLineDate> testLineList = new List<OcDebugLine.TestLineDate>();
public List<Bounds> textboundsList = new List<Bounds>();
public List<int> ColRowList
{
get
{
return _colRowList;
}
}
public Dictionary<int, List<MapCell>> DicMapCell
{
get
{
return _dicMapCell;
}
}
public Vector3 Min
{
get
{
return _min;
}
}
public Vector3 Max
{
get
{
return _max;
}
}
public List<float> VerticalSize
{
get
{
return _verticalSize;
}
}
#region//私有函数
//初始化Root
private void Init()
{
//初始化根节点 和所有render
if (_sceneRoot == null)
{
_sceneRoot = GameObject.Find(_rootName);
var debugScript = RequireComponent<OcDebugLine>(_sceneRoot);//_sceneRoot.AddComponent<OcDebugLine>();
debugScript.ocUtile = this;
}
_childs.Clear();
Renderer[] rds = _sceneRoot.GetComponentsInChildren<Renderer>(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<BoxCollider>();
if (collider == null)
{
//添加boxCollider并且缓存
node.trans.gameObject.AddComponent<MeshCollider>();
_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<OcMapDataScript>();
//初始化地图大小
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<MapCell> 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<MeshCollider>());
}
//设置数据脚本数据
List<OcMapDataScript.PortalVisableData> portalVisableList = new List<OcMapDataScript.PortalVisableData>();
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<Renderer>();
Mesh ms = null;
if (_childs[i].rd is MeshRenderer)
{
MeshFilter mf = _childs[i].trans.GetComponent<MeshFilter>();
ms = mf.sharedMesh;
_childs[i].rdType = OcRender.OC_MeshRender;
}
else if (_childs[i].rd is SkinnedMeshRenderer)
{
SkinnedMeshRenderer skinRender = _childs[i].trans.GetComponent<SkinnedMeshRenderer>();
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<Vector3>(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<MapCell> celList = new List<MapCell>();
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<Vector3>(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<MapCell> 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<float> heightList = new List<float>();
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<int> 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<string> 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<string>();
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<MapCell> 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<MeshFilter>().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<SkinnedMeshRenderer>();
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<ParticleSystemRenderer>();
SetCommpentPart(listCell, type, _childs[i]);
}
break;
}
}
}
private void SetCommpentPart(List<MapCell> 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<int> refrenceList = new List<int>();
if (node.dicRefrenceCellIds.ContainsKey((int)type))
{
refrenceList = node.dicRefrenceCellIds[(int)type];
refrenceList.Add(j);
}
else
{
refrenceList = new List<int>();
refrenceList.Add(j);
node.dicRefrenceCellIds.Add((int)type, refrenceList);
}
}
}
}
private void SetCommpentPart(List<MapCell> 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<int> refrenceList = new List<int>();
if (node.dicRefrenceCellIds.ContainsKey((int)type))
{
refrenceList = node.dicRefrenceCellIds[(int)type];
refrenceList.Add(j);
}
else
{
refrenceList = new List<int>();
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<ParticleSystem>();
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<T>(GameObject go) where T : Component
{
T ret = go.GetComponent<T>();
if (ret == null)
{
ret = go.AddComponent<T>();
}
return ret;
}
#endregion
#region//公共函数
public static void StartBaker()
{
OcclusionCullingUtil oc = new OcclusionCullingUtil();
oc.Start();
}
#endregion
}
}