void CheckCamera() { Camera cam = currentCamera; if (cam == null) { return; } currentCamPos = cam.transform.position; currentCamRot = cam.transform.rotation; _cameraHasMoved = (lastCamPos != currentCamPos || lastCamRot != currentCamRot); if (_cameraHasMoved) { lastCamPos = currentCamPos; lastCamRot = currentCamRot; if (frustumPlanes == null) { frustumPlanes = new Plane[6]; frustumPlanesDistances = new float[6]; frustumPlanesNormals = new Vector3[6]; } #if UNITY_IOS || UNITY_WEBGL #if UNITY_2017_3_OR_NEWER GeometryUtility.CalculateFrustumPlanes(cam.projectionMatrix * cam.worldToCameraMatrix, frustumPlanes); #else frustumPlanes = GeometryUtility.CalculateFrustumPlanes(cam.projectionMatrix * cam.worldToCameraMatrix); #endif #else GeometryUtilityNonAlloc.CalculateFrustumPlanes(frustumPlanes, cam.projectionMatrix * cam.worldToCameraMatrix); #endif for (int k = 0; k < 6; k++) { frustumPlanesDistances [k] = frustumPlanes [k].distance; frustumPlanesNormals [k] = frustumPlanes [k].normal; } if (frustumCorners == null || frustumCorners.Length != 4) { frustumCorners = new Vector3[4]; } cam.CalculateFrustumCorners(Misc.rectFullViewport, cam.farClipPlane, Camera.MonoOrStereoscopicEye.Mono, frustumCorners); for (int i = 0; i < 4; i++) { frustumCorners [i] = cam.transform.position + cam.transform.TransformVector(frustumCorners [i]); } shouldCheckChunksInFrustum = true; } }
VoxelChunk PopNearestChunk(int frustumCheckIteration, float acceptedDistanceSqr, float forceChunkSqrDistance, out float chunkDist) { chunkDist = 1e8f; int nearestNode = 0; float minDist = float.MaxValue; int node = chunkQueueRoot; while (node > 0) { VoxelChunk chunk = linkedChunks [node].chunk; float cdx = chunk.position.x - currentCamPos.x; float cdz = chunk.position.z - currentCamPos.z; float dist = cdx * cdx + cdz * cdz; // if chunk is within forced visible distance, return it immediately if (dist <= forceChunkSqrDistance) { chunkDist = dist; nearestNode = node; break; } // otherwise, prioritize by distance and frustum if (frustumCheckIteration != chunk.frustumCheckIteration) { chunk.frustumCheckIteration = frustumCheckIteration; Vector3 boundsMin; boundsMin.x = chunk.position.x - 8; boundsMin.y = chunk.position.y - 8; boundsMin.z = chunk.position.z - 8; Vector3 boundsMax; boundsMax.x = chunk.position.x + 8; boundsMax.y = chunk.position.y + 8; boundsMax.z = chunk.position.z + 8; chunk.visibleInFrustum = GeometryUtilityNonAlloc.TestPlanesAABB(frustumPlanesNormals, frustumPlanesDistances, ref boundsMin, ref boundsMax); } float frontDist = dist; if (!chunk.visibleInFrustum && !chunk.ignoreFrustum) { bool inDistance = frontDist <= acceptedDistanceSqr; if (onlyRenderInFrustum && !inDistance) { node = linkedChunks [node].next; continue; } frontDist += 1000000000; } if (frontDist < minDist) { minDist = frontDist; chunkDist = dist; nearestNode = node; if (frontDist < acceptedDistanceSqr) { break; } } node = linkedChunks [node].next; } if (nearestNode > 0) { if (nearestNode == chunkQueueRoot) { chunkQueueRoot = linkedChunks [nearestNode].next; linkedChunks [chunkQueueRoot].prev = 0; } else { if (linkedChunks [nearestNode].prev > 0) { linkedChunks [linkedChunks [nearestNode].prev].next = linkedChunks [nearestNode].next; } if (linkedChunks [nearestNode].next > 0) { linkedChunks [linkedChunks [nearestNode].next].prev = linkedChunks [nearestNode].prev; } } linkedChunks [nearestNode].used = false; VoxelChunk chunk = linkedChunks [nearestNode].chunk; return(chunk); } return(null); }
void CheckCuboidVisibility(Octree cuboid) { if (cuboid.exploredChildren >= 8) { return; } Vector3 cuboidCenter = cuboid.center; float dx = cuboidCenter.x - currentCamPos.x; if (dx < 0) { dx = -dx; // simple abs() } dx -= cuboid.extents; dx /= 16f; if (dx > _visibleChunksDistance) { return; } float dy = cuboidCenter.y - currentCamPos.y; if (dy < 0) { dy = -dy; } dy -= cuboid.extents; dy /= 16f; int chunksYDistance = _visibleChunksDistance >= 8 ? 8 : _visibleChunksDistance; if (dy > chunksYDistance) { return; } float dz = cuboidCenter.z - currentCamPos.z; if (dz < 0) { dz = -dz; } dz -= cuboid.extents; dz /= 16f; if (dz > _visibleChunksDistance) { return; } Vector3 cuboidMin, cuboidMax; cuboidMin.x = cuboidCenter.x - cuboid.extents; cuboidMin.y = cuboidCenter.y - cuboid.extents; cuboidMin.z = cuboidCenter.z - cuboid.extents; cuboidMax.x = cuboidCenter.x + cuboid.extents; cuboidMax.y = cuboidCenter.y + cuboid.extents; cuboidMax.z = cuboidCenter.z + cuboid.extents; bool inFrustum; if (onlyRenderInFrustum) { inFrustum = GeometryUtilityNonAlloc.TestPlanesAABB(frustumPlanesNormals, frustumPlanesDistances, ref cuboidMin, ref cuboidMax); } else { inFrustum = true; } if (inFrustum) { int cuboidSize = cuboid.extents * 2; if (cuboidSize > 16) { if (cuboid.children == null) { cuboid.Explode(); } for (int k = 0; k < 8; k++) { if (!cuboid.children [k].explored) { PushCuboid(cuboid.children [k]); } } return; } // cuboid is less than a chunk - use that chunk int chunkX, chunkY, chunkZ; GetChunkCoordinates(cuboid.center, out chunkX, out chunkY, out chunkZ); int hash = GetChunkHash(chunkX, chunkY, chunkZ); CachedChunk cachedChunk; if (cachedChunks.TryGetValue(hash, out cachedChunk)) { cachedChunk.octree = cuboid; cuboid.parent.exploredChildren++; cuboid.explored = true; VoxelChunk chunk = cachedChunk.chunk; if (chunk == null) { return; } if (chunk.isPopulated) { // If this chunk has been created but not rendered yet, request it // if (chunk.renderState == ChunkRenderState.Pending && !chunk.inqueue) { if (chunk.renderState != ChunkRenderState.RenderingComplete || !chunk.mr.enabled) { if (chunk.inqueue) { chunk.needsMeshRebuild = true; } else { ChunkRequestRefresh(chunk, false, true); } } return; } } CreateChunk(hash, chunkX, chunkY, chunkZ, false); chunkCreationCountThisFrame++; } }