Пример #1
0
        //	create quadtree buffer
        void AllocateNodeBuffer()
        {
            int  gridNum = hmSize - 1;
            long nodeCnt = 1;

            if (gridNum > terrain.leafGridSize)
            {
                for (int i = 0; gridNum > terrain.leafGridSize; i++)
                {
                    nodeCnt  += 4 << (i * 2);
                    gridNum >>= 1;
                }
            }

            //	just to ensure there isn't too many nodes
            Debug.Assert(nodeCnt < uint.MaxValue);

            nodeBuf = new QtreeNode[nodeCnt];
            nodeNum = (int)nodeCnt;

            for (int i = 0; i < nodeNum; i++)
            {
                nodeBuf[i] = new QtreeNode();
            }
        }
Пример #2
0
        public Quadtree(QuadtreeTerrain _trn, int _hmSize)
        {
            Debug.Assert(Misc.Is2Power(_hmSize - 1));

            terrain = _trn;
            hmSize  = _hmSize;

            //	create node buffer
            AllocateNodeBuffer();

            //	Initialize root node
            root            = nodeBuf[0];
            root.index      = 0;
            root.hmLeft     = 0;
            root.hmTop      = 0;
            root.hmAreaSize = (ushort)(_hmSize - 1);

            //	Generate tree recursively
            int nodeTempCnt = 1;

            BuildNode_r(0, (sbyte)eChildPos.POS_INVALID, ref nodeTempCnt);

            //	check node number
            Debug.Assert(nodeTempCnt == (int)nodeNum);

            //	Build neighbours info
            BuildNeighbours_r(0);
        }
Пример #3
0
        QuadtreeMesh CreateNodeMesh(int nodeIndex)
        {
            try
            {
                QtreeNode node = terrain.qtree.GetQtreeNode(nodeIndex);

                int rowVertNum = terrain.vertexNumInNodeRow;
                int numVert    = rowVertNum * rowVertNum;

                //	Prepare temporary buffers
                if (tempVertNum != numVert)
                {
                    //	In fact, except the first time, the code should never run into here,
                    //	for all quadtree nodes should have same number of vertex in their inborn LOD mesh
                    tempVerts   = new VertexBuffer(numVert);
                    tempVertNum = numVert;
                }

                int   gridSize = terrain.gridSize;
                float miny     = 0.0f;
                float maxy     = 0.0f;

                //	Get mesh from height blender
                if (!terrain.gemBuilder.BuildQTreeNodeMesh(node.hmLeft, node.hmTop, node.gridStep,
                                                           rowVertNum, gridSize, tempVerts, out miny, out maxy))
                {
                    Debug.Assert(false);
                    return(null);
                }

                //	calculate mesh's aabb in local space
                Rect    rcLocal = node.CalcLocalArea(terrain.gridSize);
                Vector3 mins    = new Vector3(rcLocal.xMin, miny, rcLocal.yMin);
                Vector3 maxs    = new Vector3(rcLocal.xMax, maxy, rcLocal.yMax);

                //	Create mesh object
                QuadtreeMesh nodeMesh = new QuadtreeMesh(terrain, nodeIndex, mins, maxs)
                {
                    ts = Time.realtimeSinceStartup,
                };

                //	Create height map
                nodeMesh.heights = new float[numVert];
                for (int i = 0; i < numVert; i++)
                {
                    nodeMesh.heights[i] = tempVerts.verts[i].pos.y;
                }

                //	Create vertex buffer
                nodeMesh.CreateVertCB(tempVerts);

                return(nodeMesh);
            }
            catch
            {
                Debug.LogFormat("Failed to create node mesh {0}", nodeIndex);
                return(null);
            }
        }
Пример #4
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]);
                }
            }
        }
Пример #5
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);
            }
        }
Пример #6
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]);
                }
            }
        }
Пример #7
0
        bool BuildNode_r(int nodeIndex, sbyte childPos, ref int nodeCnt)
        {
            QtreeNode node     = nodeBuf[nodeIndex];
            int       gridSize = terrain.gridSize;

            node.childPos = childPos;

            //	Check if this node meets leaf condition
            if (node.hmAreaSize <= terrain.leafGridSize)
            {
                //	This is a leaf.
                //	In fact, they should be equal !
                Debug.Assert(node.hmAreaSize == terrain.leafGridSize);

                node.inbornLOD = 0;
                node.gridStep  = 1;
                return(true);
            }

            int halfSize = node.hmAreaSize / 2;

            //	Split node and generate 4 children
            for (int i = 0; i < 4; i++)
            {
                QtreeNode newNode = nodeBuf[nodeCnt];
                newNode.index = nodeCnt++;

                newNode.parent   = node.index;
                node.children[i] = newNode.index;

                if ((i & 0x01) != 0)                   //	On right side ?
                {
                    newNode.hmLeft = node.hmLeft + halfSize;
                }
                else
                {
                    newNode.hmLeft = node.hmLeft;
                }

                if ((i & 0x02) != 0)                   //	On bottom side ?
                {
                    newNode.hmTop = node.hmTop + halfSize;
                }
                else
                {
                    newNode.hmTop = node.hmTop;
                }

                newNode.hmAreaSize = halfSize;
            }

            //	Go on for children
            for (int i = 0; i < 4; i++)
            {
                if (!BuildNode_r(node.children[i], (sbyte)i, ref nodeCnt))
                {
                    return(false);
                }
            }

            //	Check if this node needs generating mesh ?
            if (node.hmAreaSize <= terrain.maxDrawnNodeGridSize)
            {
                //	Our LOD level should be our childs +1
                node.inbornLOD = (sbyte)(nodeBuf[node.children[0]].inbornLOD + 1);
                node.gridStep  = (ushort)(1 << node.inbornLOD);

                Debug.Assert(terrain.leafGridSize == (node.hmAreaSize / node.gridStep));
            }

            return(true);
        }