/// <summary> /// Node 分割 /// </summary> /// <param name="maxLevel">限制Node最大的深度</param> /// <param name="maxItems">限制Node包含的最大数量</param> public void Distribute(int maxLevel, int maxItems) { if (Level < maxLevel && (SplitAxis != LEAF || ItemCount > maxItems)) { if (SplitAxis == LEAF) { Split(); } while (Head.Next != Tail) { OOItem i1 = Head.Next; i1.Detach(); float mid = i1.Obj.Box.Mid[SplitAxis]; float size = i1.Obj.Box.Size[SplitAxis]; if (mid + size < mSplitValue) { i1.Attach(Left); } else if (mid - size > mSplitValue) { i1.Attach(Right); } else { OOItem i2 = i1.Split(); i1.Attach(Left); i2.Attach(Right); } } } Merge(maxItems); }
public void DeleteItems() { while (Head.Next != Tail) { OOItem i1 = Head.Next; i1.Detach(); i1.Unlink(); } }
// 在该Node 添加 一个 Object public void AddObject(OOModel obj) { // Node内存储的数据为 OOItem OOItem item = new OOItem(); // OOItem 与 OOObject 进行 相互 关联 // OOItem 记录 Obj item.Obj = obj; // OOObject 链表头 添加 OOItem item.Link(obj.Head); // 将 item 挂到该 Node item.Attach(this); }
public OONode() { Head = new OOItem(); Tail = new OOItem(); Box = new OOBox(Vector3.one * float.MaxValue, Vector3.one * float.MinValue); SplitAxis = LEAF; Visible = 0; ItemCount = 0; Left = null; Right = null; Parent = null; Head.Prev = null; Head.Next = Tail; Tail.Next = null; Tail.Prev = Head; }
public OOModel(MannulDraw drawer) { Drawer = drawer; MeshFilter mf = drawer.gameObject.GetComponent <MeshFilter>(); Model = new OOMesh(mf); Box = new OOBox(Vector3.one * float.MaxValue, Vector3.one * float.MinValue); UpdateTransform(); Head = new OOItem(); Tail = new OOItem(); Tail.CNext = null; Head.CPrev = null; Head.CNext = Tail; Tail.CPrev = Head; CanOcclude = 1; // GeoDebugDrawUtils.DrawAABB(Box.Min, Box.Max); }
/// <summary> /// 截头体剔除算法入口 /// </summary> private void FrustumCull() { mMinQueue.Clear(); PushBox(Tree.Root, ref Tree.Root.Box); while (mMinQueue.Size > 0) { OONode nd = (OONode)mMinQueue.Dequeue(); if (mFrustum.Test(ref nd.Box) > 0) { nd.Distribute(mMaxLevel, mMaxItems); OOItem itm = nd.Head.Next; while (itm != nd.Tail) { OOModel obj = itm.Obj; if (obj.TouchId != Tree.TouchCounter) { obj.TouchId = Tree.TouchCounter; if (mFrustum.Test(ref obj.Box) > 0) { obj.Next = null; if (mVisible == null) { mVisible = mTail = obj; } else { mTail.Next = obj; mTail = obj; } } } itm = itm.Next; } if (nd.SplitAxis != OONode.LEAF) { PushBox(nd.Left, ref nd.Left.Box); PushBox(nd.Right, ref nd.Right.Box); } } } }
/// <summary> /// 老旧的遮挡剔除算法入口 /// </summary> private void OcclusionCullOld() { Stat[0] = Stat[1] = 0; Map.Clear(); mMinQueue.Clear(); mMaxQueue.Clear(); PushBox(Tree.Root, ref Tree.Root.Box); while (mMinQueue.Size > 0) { OONode nd = (OONode)mMinQueue.Dequeue(); nd.Distribute(mMaxLevel, mMaxItems); MinMax(ref nd.Box, ref nd.Box.Zmin, ref nd.Box.Zmax); if (nd.SplitAxis != OONode.LEAF) { if (nd.Visible != 0 || IsVisible(1, ref nd.Box, nd.Box.Zmin) != 0) { nd.Visible = 1; PushBox(nd.Left, ref nd.Left.Box); PushBox(nd.Right, ref nd.Right.Box); } else { nd.Visible = 0; if (nd.Parent != null) { nd.Parent.Visible = 0; } } } else { if (IsVisible(1, ref nd.Box, nd.Box.Zmin) != 0) { OOItem itm = nd.Head.Next; while (itm != nd.Tail) { if (itm.Obj.TouchId != Tree.TouchCounter) { itm.Obj.TouchId = Tree.TouchCounter; OOModel obj = itm.Obj; float dis = -Vector3.Dot(obj.Box.Mid, mLook); float d = Vector3.Dot(mAbsLook, obj.Box.Size); obj.Box.Zmin = dis - d; obj.Box.Zmax = dis + d; if (IsVisible(0, ref obj.Box, 0) != 0) { mMaxQueue.Enqueue(obj.Box.Zmax, obj, obj.Box.Zmax); obj.Next = null; if (mVisible == null) { mVisible = mTail = obj; } else { mTail.Next = obj; mTail = obj; } } } itm = itm.Next; } } if (nd.Parent != null) { nd.Parent.Visible = 0; } } } }
/// <summary> /// 遮挡剔除 算法入口 /// </summary> private void OcclusionCull() { Stat[0] = Stat[1] = 0; Map.Clear(); mMinQueue.Clear(); mMaxQueue.Clear(); PushBox2(Tree.Root, ref Tree.Root.Box); // 按 负无穷范数(最小值) 作为优先权中 遍历 while (mMinQueue.Size > 0) { OONode nd = (OONode)mMinQueue.Dequeue(); nd.Distribute(mMaxLevel, mMaxItems); MinMax(ref nd.Box, ref nd.Box.Zmin, ref nd.Box.Zmax); if (nd.SplitAxis != OONode.LEAF) // 非叶节点 { // 该节点存在两个子节点 // 如果 该节点 Visible 为可见 ,则不需要进一步计算 // 如果 该结点 Visible 为不可见,则进一步计算判断可见性 // KDTree的Node不是每次重新分配,带有一定的缓存功能。所以, Visible 可以加速计算 // 存在一个问题:如果所有的物体都分到了一个Node中,可能会不停的划分到最大深度。后期需要优化 if ((nd.Visible != 0) || IsVisible(1, ref nd.Box, nd.Box.Zmin) != 0) { // 标记为可见 nd.Visible = 1; // 父节点可见,接下来判断左右孩子结点的可见性 PushBox2(nd.Left, ref nd.Left.Box); PushBox2(nd.Right, ref nd.Right.Box); } else { // 标记为不可见 nd.Visible = 0; if (nd.Parent != null) { nd.Parent.Visible = 0; } } } else // 叶节点 { // 叶节点的Box测试 if (IsVisible(1, ref nd.Box, nd.Box.Zmin) != 0) { OOItem itm = nd.Head.Next; // 保存需要绘制的物体,以 Zmax 为优先级排序 while (itm != nd.Tail) { if (itm.Obj.TouchId != Tree.TouchCounter) { itm.Obj.TouchId = Tree.TouchCounter; OOModel obj = itm.Obj; MinMax(ref obj.Box, ref obj.Box.Zmin, ref obj.Box.Zmax); // 查询物体的Box是否可见 if (IsVisible(0, ref obj.Box, 0) != 0) { // 如果一个物体的Box可见,则为待绘制的物体 mMaxQueue.Enqueue(obj.Box.Zmax, obj, obj.Box.Zmax); obj.Next = null; if (mVisible == null) { mVisible = mTail = obj; } else { mTail.Next = obj; mTail = obj; } } } itm = itm.Next; } } if (nd.Parent != null) { nd.Parent.Visible = 0; } } } #if TEST_DRAW VisualKDTree(Tree.Root); if (mMaxQueue.Size > 0) { DebugUtils.Info("OcclusionCull", "Before Max Left: ", mMaxQueue.Size); FlushOccluders(float.MaxValue); DebugUtils.Info("OcclusionCull", "After Max Left: ", mMaxQueue.Size); } Map.DrawScreenShot(); #endif }