// update mesh cache void UpdateMeshCache() { if (Time.realtimeSinceStartup < lastUpdateCacheTime + 1.0f) { return; } lastUpdateCacheTime = Time.realtimeSinceStartup; const float maxStayTime = 30.0f; // 30s List <int> rmList = new List <int>(); foreach (DictionaryEntry de in meshTable) { QuadtreeMesh mesh = de.Value as QuadtreeMesh; if (Time.realtimeSinceStartup > mesh.ts + maxStayTime) { mesh.Destroy(); rmList.Add((int)de.Key); } } foreach (int nodeIndex in rmList) { meshTable.Remove(nodeIndex); } }
// request a node's mesh public bool RequestNodeMesh(int nodeIndex) { Debug.Assert(nodeIndex >= 0); // Check in mesh cache QuadtreeMesh mesh = meshTable[nodeIndex] as QuadtreeMesh; if (mesh != null) { // update time stamp mesh.ts = Time.realtimeSinceStartup; return(true); } // Single thread loading mesh = CreateNodeMesh(nodeIndex); if (mesh != null) { // Push to table meshTable.Add(nodeIndex, mesh); return(true); } else { Debug.Assert(mesh != null); return(false); } }
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); } }
// Get mesh for a quadtree node public QuadtreeMesh GetNodeMesh(int nodeIndex) { Debug.Assert(nodeIndex >= 0); QuadtreeMesh mesh = meshTable[nodeIndex] as QuadtreeMesh; return(mesh); }
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]); } } }