/// <summary> /// 此 Node 分割 /// </summary> private void Split() { // 左右孩子创建 Left = new OONode(); Right = new OONode(); // 记录孩子Node的深度值 Right.Level = Left.Level = Level + 1; // 记录孩子Node的父节点 Left.Parent = Right.Parent = this; // 获取分割轴 SplitAxis = GetSplitAxis(ref Box.Size); // 获取分割轴的分割值 mSplitValue = Box.Mid[SplitAxis]; // 计算孩子结点的包围盒 size. Left.Box = Right.Box = Box; float half = 0.5f * Box.Size[SplitAxis]; // 孩子包围盒的此寸为父节点的一半 Left.Box.Size[SplitAxis] = Right.Box.Size[SplitAxis] = half; // 在分割轴上, 加减 size,得到新的 中心点 Left.Box.Mid[SplitAxis] = Box.Mid[SplitAxis] - half; Right.Box.Mid[SplitAxis] = Box.Mid[SplitAxis] + half; Right.Box.ToMinMax(); Left.Box.ToMinMax(); }
/// <summary> /// 删除指定结点 /// </summary> /// <param name="nd">待删除结点</param> private void DeleteNodes(OONode nd) { if (nd == null) { return; } nd.DeleteItems(); DeleteNodes(nd.Left); DeleteNodes(nd.Right); }
private void VisualKDTree(OONode node) { node.DrawAABB(); if (node.Left != null) { VisualKDTree(node.Left); } if (node.Right != null) { VisualKDTree(node.Right); } }
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; }
/// <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> /// <param name="maxItems"></param> private void Merge(int maxItems) { if (SplitAxis == LEAF) { return; } if (Left.SplitAxis != LEAF || Right.SplitAxis != LEAF) { return; } if (Left.ItemCount + Right.ItemCount >= maxItems) { return; } DoubleCounter++; OOItem i1; while (Left.Head.Next != Left.Tail) { i1 = Left.Head.Next; i1.Detach(); i1.Attach(this); i1.Obj.DoubleId = DoubleCounter; } while (Right.Head.Next != Right.Tail) { i1 = Right.Head.Next; i1.Detach(); if (i1.Obj.DoubleId != DoubleCounter) { i1.Attach(this); } else { i1.Unlink(); } } Left = Right = null; SplitAxis = LEAF; }
/// <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 }
public OOKDTree() { Root = new OONode(); Root.Level = 0; }