public void AddObject(MannulDraw obj) { if (!DrawObjects.ContainsKey(obj.GetInstanceID())) { // object OOModel oobj = new OOModel(obj); oobj.SetObjectId(obj.GetInstanceID()); // culler Culler.Add(oobj); DrawObjects.Add(oobj.GetObjectId(), oobj); } }
/// <summary> /// 将全部落入 distance 的AABB对应的 Item 物体 绘制到缓冲区 /// </summary> /// <param name="distance">刷新距离值</param> private void FlushOccluders(float distance) { while (mMaxQueue.Size > 0 && ((OOModel)mMaxQueue.Peek()).Box.Zmax <= distance) { OOModel obj = (OOModel)mMaxQueue.Dequeue(); if (obj.CanOcclude == 0) { continue; } DrawOccluder(obj); } }
// 在该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); }
/// <summary> /// 根据指定的算法运行 /// </summary> /// <param name="mode">运行模式</param> public void FindVisible(int mode) { mVisible = mTail = null; Tree.TouchCounter++; switch (mode) { case OOCE_FRUSTUM_CULLING: FrustumCull(); break; case OOCE_OCCLUSION_CULLING: OcclusionCull(); break; case OOCE_OCCLUSION_CULLING_OLD: OcclusionCullOld(); break; } }
public void Refresh(OOModel obj) { OONode nd; nd = obj.Head.CNext.Node; Vector3 absV = (obj.Box.Mid - nd.Box.Mid).Abs(); Vector3 sizeV = nd.Box.Size - obj.Box.Size; if (absV.Less(sizeV)) { return; } while (nd.Parent != null && absV.AnyGreater(sizeV)) { nd = nd.Parent; } obj.Detach(); nd.AddObject(obj); }
/// <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="obj">待绘制物体</param> private void DrawOccluder(OOModel obj) { // 获取物体的模型 OOMesh mdl = obj.Model; // 计算 ModelView 矩阵.注意:这里需要相机的变换矩阵的逆矩阵 // 另一问题:左手系和右手系问题。 Matrix4x4 modelViewMatrix = mView * obj.ModelWorldMatrix; // 变换mesh的顶点到 相机空间 和 裁剪空间 for (int i = 0; i < mdl.NumVert; i++) { // Vector3 p = obj.ModelWorldMatrix.MultiplyPoint3x4(mdl.Vertices[i]); Vector4 tmp = modelViewMatrix.MultiplyPoint3x4(mdl.Vertices[i]); mdl.CameraSpaceVertices[i] = tmp; tmp.w = 1; mdl.ClipSpaceVertices[i] = mProject * tmp; } int xmin = 100000; int xmax = 0; int ymin = 100000; int ymax = 0; // 遍历所有面 for (int i = 0; i < mdl.NumFace; i++) { // 面索引 int p1 = mdl.Faces[i][0]; int p2 = mdl.Faces[i][1]; int p3 = mdl.Faces[i][2]; // 计算法向量。此处可以优化成:初始化时计算,而后变换一下法向量即可。 // 构建右手坐标系 逆时针为正方向 Vector3 a = mdl.CameraSpaceVertices[p2] - mdl.CameraSpaceVertices[p1]; Vector3 b = mdl.CameraSpaceVertices[p3] - mdl.CameraSpaceVertices[p1]; Vector3 n = Vector3.Cross(a, b); // 背面剔除。可计算相机的朝向,然后与三角面的法线计算即可。 // 此处实际上计算 Camera 空间 原点到平面的距离是否小于0 if (Vector3.Dot(n, mdl.CameraSpaceVertices[p1]) < 0) { mClip.mClipSpaceVertices[0] = mdl.ClipSpaceVertices[p1]; mClip.mClipSpaceVertices[1] = mdl.ClipSpaceVertices[p2]; mClip.mClipSpaceVertices[2] = mdl.ClipSpaceVertices[p3]; // 裁剪计算 int nv = mClip.ClipAndProject(3); #if TEST_DRAW_ONE DebugUtils.Info("ClipAndProject", string.Format("world1({0}, {1})", mClip.mScreenSpaceVertices[0][0], mClip.mScreenSpaceVertices[0][1])); DebugUtils.Info("ClipAndProject", string.Format("world2({0}, {1})", mClip.mScreenSpaceVertices[1][0], mClip.mScreenSpaceVertices[1][1])); DebugUtils.Info("ClipAndProject", string.Format("world3({0}, {1})", mClip.mScreenSpaceVertices[2][0], mClip.mScreenSpaceVertices[2][1])); #endif // 裁剪判断 if (nv > 2) { // 裁剪后,计算屏幕区域的AABB for (int j = 0; j < nv; j++) { if (mClip.mScreenSpaceVertices[j][0] < xmin) { xmin = mClip.mScreenSpaceVertices[j][0]; } else { if (mClip.mScreenSpaceVertices[j][0] > xmax) { xmax = mClip.mScreenSpaceVertices[j][0]; } } if (mClip.mScreenSpaceVertices[j][1] < ymin) { ymin = mClip.mScreenSpaceVertices[j][1]; } else if (mClip.mScreenSpaceVertices[j][1] > ymax) { ymax = mClip.mScreenSpaceVertices[j][1]; } } // 绘制多边形到缓冲 Map.DrawPolygon(mClip.mScreenSpaceVertices, nv); } } } // 设置该区域内有被覆盖 Map.SetDirtyRectangle(xmin, ymin, xmax, ymax); }
/// <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 }
/// <summary> /// 重置下一个可见物体为当前物体 /// </summary> /// <returns></returns> public int GetNextObject() { mTemp = mTemp.Next; return(mTemp != null ? 1 : 0); }
/// <summary> /// 获得当前第一个可见物体 /// </summary> /// <returns></returns> public int GetFirstObject() { mTemp = mVisible; return(mTemp != null ? 1 : 0); }
/// <summary> /// 删除KDTree中指定的Object /// </summary> /// <param name="obj">待删除物体</param> public void Remove(OOModel obj) { Tree.Delete(obj); }
/// <summary> /// 添加物体到KDTree /// </summary> /// <param name="obj">待添加物体</param> public void Add(OOModel obj) { Tree.Add(obj); }
public void Delete(OOModel obj) { obj.Detach(); }
public void Add(OOModel obj) { Root.AddObject(obj); }