/// <summary> /// Gets the chunk if exits or create it if forceCreation is set to true. /// </summary> /// <returns><c>true</c>, if chunk fast was gotten, <c>false</c> otherwise.</returns> /// <param name="chunkX">Chunk x.</param> /// <param name="chunkY">Chunk y.</param> /// <param name="chunkZ">Chunk z.</param> /// <param name="chunk">Chunk.</param> /// <param name="createIfNotAvailable">If set to <c>true</c> force creation if chunk doesn't exist.</param> bool GetChunkFast(int chunkX, int chunkY, int chunkZ, out VoxelChunk chunk, bool createIfNotAvailable = false) { lock (lockLastChunkFetch) { if (lastChunkFetchX == chunkX && lastChunkFetchY == chunkY && lastChunkFetchZ == chunkZ && (object)lastChunkFetch != null) { chunk = lastChunkFetch; return(true); } } int hash = GetChunkHash(chunkX, chunkY, chunkZ); STAGE = 501; CachedChunk cachedChunk; cachedChunks.TryGetValue(hash, out cachedChunk); bool exists = (object)cachedChunk != null; if (createIfNotAvailable) { if (!exists) { STAGE = 502; // not yet created, create it cachedChunk = new CachedChunk(); cachedChunk.chunk = CreateChunk(hash, chunkX, chunkY, chunkZ, false); exists = true; } if ((object)cachedChunk.chunk == null) // chunk is really empty, create it with empty space { STAGE = 503; cachedChunk.chunk = CreateChunk(hash, chunkX, chunkY, chunkZ, true); } } STAGE = 0; if (exists) { chunk = cachedChunk.chunk; lock (lockLastChunkFetch) { lastChunkFetchX = chunkX; lastChunkFetchY = chunkY; lastChunkFetchZ = chunkZ; lastChunkFetch = chunk; } return(chunk != null); } else { chunk = null; return(false); } }
/// <summary> /// Destroys all chunks and internal engine structures - should be used only during shutdown /// </summary> public void DisposeAll() { StopGenerationThread(); DestroyNavMesh(); chunksPool = null; chunksPoolCurrentIndex = -1; chunksPoolLoadIndex = 0; chunksPoolFetchNew = true; lastChunkFetch = null; // Empty mesh jobs if (meshJobs != null) { for (int k = 0; k < meshJobs.Length; k++) { if (meshJobs [k].vertices != null) { meshJobs [k].vertices.Clear(); meshJobs [k].vertices = null; } if (meshJobs [k].uv0 != null) { meshJobs [k].uv0.Clear(); meshJobs [k].uv0 = null; } if (meshJobs [k].uv2 != null) { meshJobs [k].uv2.Clear(); meshJobs [k].uv2 = null; } if (meshJobs [k].colors != null) { meshJobs [k].colors.Clear(); meshJobs [k].colors = null; } if (meshJobs [k].normals != null) { meshJobs [k].normals.Clear(); meshJobs [k].normals = null; } if (meshJobs [k].buffers != null) { for (int j = 0; j < MAX_MATERIALS_PER_CHUNK; j++) { if (meshJobs [k].buffers [j].indices != null) { meshJobs [k].buffers [j].indices.Clear(); meshJobs [k].buffers [j].indices = null; meshJobs [k].buffers [j].indicesArray = null; } } } if (meshJobs [k].colliderVertices != null) { meshJobs [k].colliderVertices.Clear(); meshJobs [k].colliderVertices = null; } if (meshJobs [k].colliderIndices != null) { meshJobs [k].colliderIndices.Clear(); meshJobs [k].colliderIndices = null; } if (meshJobs [k].navMeshVertices != null) { meshJobs [k].navMeshVertices.Clear(); meshJobs [k].navMeshVertices = null; } if (meshJobs [k].navMeshIndices != null) { meshJobs [k].navMeshIndices.Clear(); meshJobs [k].navMeshIndices = null; } if (meshJobs [k].mivs != null) { meshJobs [k].mivs.Clear(); meshJobs [k].mivs = null; } } } // Destroy chunks if (cachedChunks != null) { foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks) { CachedChunk cc = kv.Value; if (cc != null && cc.chunk != null) { DestroyImmediate(cc.chunk.gameObject); } } cachedChunks.Clear(); } chunkRequestLast = -1; cachedChunks = null; if (worldRoot != null) { while (worldRoot.childCount > 0) { DestroyImmediate(worldRoot.GetChild(0).gameObject); } } worldRoot = null; cloudsRoot = null; chunksRoot = null; fxRoot = null; lastChunkX = int.MaxValue; // Clear heightmap if (heightMapCache != null) { heightMapCache.Clear(); } // Clear render queue chunkQueueRoot = 0; lastChunkDistanceSqr = 0; ClearStats(); // reset voxel definitions state values if (voxelDefinitions != null) { for (int k = 0; k < voxelDefinitions.Length; k++) { VoxelDefinition vd = voxelDefinitions [k]; if (vd != null) { vd.Reset(); } } } initialized = false; }
/// <summary> /// Creates the chunk. /// </summary> /// <returns>The chunk.</returns> /// <param name="hash">Hash.</param> /// <param name="chunkX">Chunk x.</param> /// <param name="chunkY">Chunk y.</param> /// <param name="chunkZ">Chunk z.</param> /// <param name="createEmptyChunk">If set to <c>true</c> create empty chunk.</param> /// <param name="complete">If set to <c>true</c> detail generators will fire as well as OnChunkCreated event. Chunk will be marked as populated and a refresh will be triggered if within view distance.</param> VoxelChunk CreateChunk(int hash, int chunkX, int chunkY, int chunkZ, bool createEmptyChunk, bool complete = true) { STAGE = 101; Vector3 position; position.x = chunkX * 16 + 8; position.y = chunkY * 16 + 8; position.z = chunkZ * 16 + 8; STAGE = 102; CachedChunk cachedChunk; // Create entry in the dictionary if (!cachedChunks.TryGetValue(hash, out cachedChunk)) { cachedChunk = new CachedChunk(); cachedChunks [hash] = cachedChunk; } STAGE = 103; VoxelChunk chunk; if ((object)cachedChunk.chunk == null) { // Fetch a new entry in the chunks pool if (chunksPoolFetchNew) { chunksPoolFetchNew = false; FetchNewChunkIndex(position); } chunk = chunksPool [chunksPoolCurrentIndex]; } else { chunk = cachedChunk.chunk; } // Paint voxels bool chunkHasContents = false; chunk.position = position; STAGE = 104; if (createEmptyChunk) { chunk.isAboveSurface = CheckIfChunkAboveTerrain(position); } else { if (world.infinite || (position.x >= -world.extents.x && position.x <= world.extents.x && position.y >= -world.extents.y && position.y <= world.extents.y && position.z >= -world.extents.z && position.z <= world.extents.z)) { if (OnChunkBeforeCreate != null) { // allows a external function to fill the contents of this new chunk bool isAboveSurface = true; OnChunkBeforeCreate(position, out chunkHasContents, chunk.voxels, out isAboveSurface); chunk.isAboveSurface = isAboveSurface; } if (!chunkHasContents) { if (position.y < CLOUDS_SPECIAL_ALTITUDE) { chunkHasContents = world.terrainGenerator.PaintChunk(chunk); } if (!chunkHasContents) { chunk.isAboveSurface = true; } } } } STAGE = 105; VoxelChunk nchunk; if (chunkHasContents || createEmptyChunk) { // lit chunk if not global illumination if (!effectiveGlobalIllumination) { chunk.ClearLightmap(15); } chunksPoolFetchNew = true; chunksCreated++; cachedChunk.chunk = chunk; if (complete) { chunk.isPopulated = true; // Check for detail generators if (worldHasDetailGenerators) { for (int d = 0; d < world.detailGenerators.Length; d++) { if (world.detailGenerators [d].enabled) { world.detailGenerators [d].AddDetail(chunk); } } } if (chunkHasContents) { // if chunk is near camera, request a render refresh bool sendRefresh = (chunkX >= visible_xmin && chunkX <= visible_xmax && chunkZ >= visible_zmin && chunkZ <= visible_zmax && chunkY >= visible_ymin && chunkY <= visible_ymax); if (sendRefresh) { ChunkRequestRefresh(chunk, false, true); } // Check if neighbours are inconclusive because this chunk was not present nchunk = chunk.bottom; if (nchunk != null && (nchunk.inconclusiveNeighbours & CHUNK_TOP) != 0) { ChunkRequestRefresh(nchunk, true, true); } nchunk = chunk.top; if (nchunk != null && (nchunk.inconclusiveNeighbours & CHUNK_BOTTOM) != 0) { ChunkRequestRefresh(nchunk, true, true); } nchunk = chunk.left; if (nchunk != null && (nchunk.inconclusiveNeighbours & CHUNK_RIGHT) != 0) { ChunkRequestRefresh(nchunk, true, true); } nchunk = chunk.right; if (nchunk != null && (nchunk.inconclusiveNeighbours & CHUNK_LEFT) != 0) { ChunkRequestRefresh(nchunk, true, true); } nchunk = chunk.back; if (nchunk != null && (nchunk.inconclusiveNeighbours & CHUNK_FORWARD) != 0) { ChunkRequestRefresh(nchunk, true, true); } nchunk = chunk.forward; if (nchunk != null && (nchunk.inconclusiveNeighbours & CHUNK_BACK) != 0) { ChunkRequestRefresh(nchunk, true, true); } } else { chunk.renderState = ChunkRenderState.RenderingComplete; } if ((object)OnChunkAfterCreate != null) { OnChunkAfterCreate(chunk); } } STAGE = 0; return(chunk); } else { chunk.renderState = ChunkRenderState.RenderingComplete; STAGE = 0; return(null); } }
/// <summary> /// Destroys all chunks and internal engine structures - should be used only during shutdown /// </summary> public void DisposeAll() { StopGenerationThreads(); DisposeRenderer(); DestroyNavMesh(); chunksPool = null; chunksPoolCurrentIndex = -1; chunksPoolLoadIndex = 0; chunksPoolFetchNew = true; lastChunkFetch = null; // Destroy chunks if (cachedChunks != null) { foreach (KeyValuePair <int, CachedChunk> kv in cachedChunks) { CachedChunk cc = kv.Value; if (cc != null && cc.chunk != null) { if (cc.chunk.mf != null && cc.chunk.mf.sharedMesh != null) { DestroyImmediate(cc.chunk.mf.sharedMesh); } if (cc.chunk.mc != null && cc.chunk.mc.sharedMesh != null) { DestroyImmediate(cc.chunk.mc.sharedMesh); } if (cc.chunk.navMesh != null) { DestroyImmediate(cc.chunk.navMesh); } } } cachedChunks.Clear(); } chunkRequestLast = -1; cachedChunks = null; DisposeTextures(); if (worldRoot != null) { while (worldRoot.childCount > 0) { DestroyImmediate(worldRoot.GetChild(0).gameObject); } } worldRoot = null; cloudsRoot = null; chunksRoot = null; DestroyParticles(); lastChunkX = int.MaxValue; // Clear heightmap if (heightMapCache != null) { heightMapCache.Clear(); } // Clear render queue chunkQueueRoot = 0; lastChunkDistanceSqr = 0; ClearStats(); // reset voxel definitions state values if (voxelDefinitions != null) { for (int k = 0; k < voxelDefinitions.Length; k++) { VoxelDefinition vd = voxelDefinitions [k]; if (vd != null) { vd.Reset(); } } } Resources.UnloadUnusedAssets(); GC.Collect(); initialized = false; }