Пример #1
0
        void UpdateNodeLOD_r(int nodeIndex, sbyte areaLOD)
        {
            QtreeNode node = nodeBuf[nodeIndex];

            //	Sync node's update counter
            node.updateCnt = updateCnt;
            node.areaLOD   = areaLOD;
            node.outRange  = false;

            //	node's area in terrain's local space
            Rect rcLocal = node.CalcLocalArea(terrain.gridSize);

            //	Calculate the distance from m_vUpdateCenter to node's border
            float distx      = Mathf.Abs((rcLocal.xMin + rcLocal.xMax) * 0.5f - updateCenter.x);
            float distz      = Mathf.Abs((rcLocal.yMin + rcLocal.yMax) * 0.5f - updateCenter.z);
            float min_distx  = distx - rcLocal.width * 0.5f;
            float min_distz  = distz - rcLocal.height * 0.5f;
            float check_dist = min_distx > min_distz ? min_distx : min_distz;

            if (check_dist > terrain.viewDistance)
            {
                //	this node is out of view distance
                node.outRange = true;

                //	we stop the recursively process only when this node and it's parent
                //	are both out of view distance. this is a tip to avoid node flashing
                //	on the border of view distance.
                if (node.parent >= 0 && nodeBuf[node.parent].outRange)
                {
                    node.meshState = (byte)eNodeMeshState.TO_UNLOAD;
                    return;
                }
            }

            if (node.inbornLOD < 0)
            {
                //	the node hasn't mesh at all
                node.meshState = (byte)eNodeMeshState.TO_UNLOAD;
                Debug.Assert(!node.IsLeaf());

                //	Go on for children
                for (int i = 0; i < 4; i++)
                {
                    UpdateNodeLOD_r(node.children[i], areaLOD);
                }
            }
            else if (areaLOD >= 0)
            {
                //	parent's mesh is to be loaded, so this node needn't mesh any more.
                node.meshState = (byte)eNodeMeshState.TO_UNLOAD;

                //	we can stop the recursive process here now (not go into children anymore),
                //	however we can't stop at parent node early! because this node's neighbours need
                //	its areaLOD to decide whether their border mesh should do LOD grade-up or not.
                //	if (!node.IsLeaf())
                //	{
                //      for (int i = 0; i < 4; i++)
                //          UpdateNodeLOD_r(node.children[i], areaLOD);
                //  }
            }
            else
            {
                //	Check which LOD grade this node locates in
                int lod_grade;
                for (lod_grade = lodGradeNum - 1; lod_grade >= 0; lod_grade--)
                {
                    if (check_dist > lodDists[lod_grade])
                    {
                        break;
                    }
                }

                Debug.Assert(lod_grade >= 0);

                if (lod_grade < node.inbornLOD)
                {
                    //	This node totally or partly locates in lower LOD grade area,
                    //	we should render its children
                    node.meshState = (byte)eNodeMeshState.TO_UNLOAD;

                    //	this shouldn't be a leaf
                    Debug.Assert(!node.IsLeaf());

                    //	go on for children
                    for (int i = 0; i < 4; i++)
                    {
                        UpdateNodeLOD_r(node.children[i], areaLOD);
                    }

                    //	If only part of this node in lower LOD grade area, some
                    //	children may need our mesh to do LOD grade-up render.
                    for (int i = 0; i < 4; i++)
                    {
                        if (nodeBuf[node.children[i]].meshState == (byte)eNodeMeshState.LOD_GRADEUP)
                        {
                            node.meshState = (byte)eNodeMeshState.LOAD_FOR_CHILD;
                            break;
                        }
                    }
                }
                else if (lod_grade == node.inbornLOD)
                {
                    //	use node's inborn LOD
                    node.meshState = (byte)eNodeMeshState.LOAD_INBORN;
                    node.areaLOD   = node.inbornLOD;

                    //	Go on to tell all children that they needn't loading mesh
                    if (!node.IsLeaf())
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            UpdateNodeLOD_r(node.children[i], node.areaLOD);
                        }
                    }
                }
                else if (lod_grade == nodeBuf[node.parent].inbornLOD)
                {
                    //	LOD grade up, use parent's LOD
                    node.meshState = (byte)eNodeMeshState.LOD_GRADEUP;
                    node.areaLOD   = nodeBuf[node.parent].inbornLOD;

                    //	Go on to tell all children that they needn't loading mesh
                    if (!node.IsLeaf())
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            UpdateNodeLOD_r(node.children[i], node.areaLOD);
                        }
                    }
                }
                else
                {
                    //	Shouldn't go here !
                    Debug.Assert(false);
                }
            }

            //	Stream in/out node mesh according to it's rendering flag
            if (node.meshState == (byte)eNodeMeshState.LOAD_INBORN ||
                node.meshState == (byte)eNodeMeshState.LOAD_FOR_CHILD)
            {
                terrain.meshMan.RequestNodeMesh(nodeIndex);
            }
        }
Пример #2
0
        void CollectRenderNodes_r(int nodeIndex)
        {
            QtreeNode node = nodeBuf[nodeIndex];

            //	Only render node whose info is up-to-date
            if (node.updateCnt != updateCnt)
            {
                //	If a node isn't up-to-date, all it's children must not be.
                return;
            }

            //	do camera cull with node's AABB at first, but node may not have min-y and max-y
            //	at this moment if it's mesh wasn't built, so now we only do a conservative check
            //	with x and z axis.
            //	NOTE: we ONLY consider tranlation of terrain, but not rotation and scale
            Vector3 offset  = terrain.transform.position;
            Rect    rcLocal = node.CalcLocalArea(terrain.gridSize);
            Vector3 mins    = new Vector3(rcLocal.xMin + offset.x, -10000.0f, rcLocal.yMin + offset.z);
            Vector3 maxs    = new Vector3(rcLocal.xMax + offset.x, 10000.0f, rcLocal.yMax + offset.z);
            Bounds  aabb    = new Bounds();

            aabb.SetMinMax(mins, maxs);

            if (!GeometryUtility.TestPlanesAABB(camPlanes, aabb))
            {
                return;                    //	The whole node isn't visible
            }
            if (node.meshState == (byte)eNodeMeshState.LOAD_INBORN ||
                node.meshState == (byte)eNodeMeshState.LOD_GRADEUP)
            {
                //	Get index buffer according to neighbour's LOD grade
                int sideMask = 0;
                for (int i = 0; i < 4; i++)
                {
                    if (node.neighbour[i] < 0)
                    {
                        continue;
                    }

                    QtreeNode neighbour = nodeBuf[node.neighbour[i]];
                    if (neighbour.updateCnt == updateCnt && neighbour.areaLOD > node.areaLOD)
                    {
                        sideMask |= (1 << i);
                    }
                }

                //	This node's may be drawn
                ComputeBuffer _vertCB  = null;
                ComputeBuffer _indexCB = null;
                QuadtreeMesh  nodeMesh = null;

                if (node.meshState == (byte)eNodeMeshState.LOAD_INBORN)
                {
                    nodeMesh = terrain.meshMan.GetNodeMesh(nodeIndex);
                    if (nodeMesh != null)
                    {
                        _vertCB  = nodeMesh.vertCB;
                        _indexCB = terrain.trnRes.GetNodeIndexCB(sideMask, -1);
                    }
                }
                else if (node.meshState == (byte)eNodeMeshState.LOD_GRADEUP)
                {
                    //	LOD grade-up rendering, use parent's mesh
                    Debug.Assert(node.parent >= 0);
                    nodeMesh = terrain.meshMan.GetNodeMesh(node.parent);
                    if (nodeMesh != null)
                    {
                        _vertCB  = nodeMesh.vertCB;
                        _indexCB = terrain.trnRes.GetNodeIndexCB(sideMask, node.childPos);
                    }
                }

                if (_vertCB != null && _indexCB != null)
                {
                    //	do camera cull again with more precise aabb
                    mins.y = nodeMesh.aabb.min.y + offset.y;
                    maxs.y = nodeMesh.aabb.max.y + offset.y;
                    aabb.SetMinMax(mins, maxs);
                    if (!GeometryUtility.TestPlanesAABB(camPlanes, aabb))
                    {
                        return;                            // The whole node isn't visible
                    }
                    //	Push node to rendering collector
                    DRAWDATA drawData = new DRAWDATA()
                    {
                        node     = node,
                        nodeAABB = aabb,
                        vertCB   = _vertCB,
                        indexCB  = _indexCB,
                    };

                    curRenderer.PushDrawData(drawData);
                }
            }

            if (!node.IsLeaf())
            {
                //	Go on for children
                for (int i = 0; i < 4; i++)
                {
                    CollectRenderNodes_r(node.children[i]);
                }
            }
        }
Пример #3
0
        void BuildNeighbours_r(int nodeIndex)
        {
            QtreeNode node = nodeBuf[nodeIndex];

            //	set our parent's neighbour's child as our neighbour ...
            Action <QtreeNode, int, int, int> SetParentNeighbourChildAsNeighbour =
                (QtreeNode theNode, int target, int neighbour, int child) =>
            {
                int parentNbr = nodeBuf[theNode.parent].neighbour[neighbour];
                if (parentNbr >= 0)
                {
                    theNode.neighbour[target] = nodeBuf[parentNbr].children[child];
                }
                else
                {
                    theNode.neighbour[target] = -1;
                }
            };

            if (node.parent < 0)
            {
                //	root node
                node.neighbour[(int)eNeighbour.LEFT]   = -1;
                node.neighbour[(int)eNeighbour.TOP]    = -1;
                node.neighbour[(int)eNeighbour.RIGHT]  = -1;
                node.neighbour[(int)eNeighbour.BOTTOM] = -1;
            }
            else
            {
                QtreeNode parentNode = nodeBuf[node.parent];

                switch ((eChildPos)node.childPos)
                {
                case eChildPos.POS_LT:
                {
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.LEFT, (int)eNeighbour.LEFT, (int)eChildPos.POS_RT);
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.TOP, (int)eNeighbour.TOP, (int)eChildPos.POS_LB);
                    node.neighbour[(int)eNeighbour.RIGHT]  = parentNode.children[(int)eChildPos.POS_RT];
                    node.neighbour[(int)eNeighbour.BOTTOM] = parentNode.children[(int)eChildPos.POS_LB];
                    break;
                }

                case eChildPos.POS_RT:
                {
                    node.neighbour[(int)eNeighbour.LEFT] = parentNode.children[(int)eChildPos.POS_LT];
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.TOP, (int)eNeighbour.TOP, (int)eChildPos.POS_RB);
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.RIGHT, (int)eNeighbour.RIGHT, (int)eChildPos.POS_LT);
                    node.neighbour[(int)eNeighbour.BOTTOM] = parentNode.children[(int)eChildPos.POS_RB];
                    break;
                }

                case eChildPos.POS_LB:
                {
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.LEFT, (int)eNeighbour.LEFT, (int)eChildPos.POS_RB);
                    node.neighbour[(int)eNeighbour.TOP]   = parentNode.children[(int)eChildPos.POS_LT];
                    node.neighbour[(int)eNeighbour.RIGHT] = parentNode.children[(int)eChildPos.POS_RB];
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.BOTTOM, (int)eNeighbour.BOTTOM, (int)eChildPos.POS_LT);
                    break;
                }

                case eChildPos.POS_RB:
                {
                    node.neighbour[(int)eNeighbour.LEFT] = parentNode.children[(int)eChildPos.POS_LB];
                    node.neighbour[(int)eNeighbour.TOP]  = parentNode.children[(int)eChildPos.POS_RT];
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.RIGHT, (int)eNeighbour.RIGHT, (int)eChildPos.POS_LB);
                    SetParentNeighbourChildAsNeighbour(node, (int)eNeighbour.BOTTOM, (int)eNeighbour.BOTTOM, (int)eChildPos.POS_RT);
                    break;
                }

                default:
                {
                    //	Invalid child position
                    Debug.Assert(false);
                    return;
                }
                }
            }

            if (!node.IsLeaf())
            {
                //	Go on for children
                for (int i = 0; i < 4; i++)
                {
                    BuildNeighbours_r(node.children[i]);
                }
            }
        }