public void UnregisterPendingStructures() { // TODO: This is not exactly optimal. A lot of iterations for one mutex. On the other hand, I expect only // a small amount of structures stored here. Definitelly not hundreds or more. But there's a room for // improvement... lock (pendingStructureMutex) { // Let's see whether we can unload any positions for (int i = 0; i < pendingStructureInfo.Count;) { StructureInfo info = pendingStructureInfo[i]; Vector3Int pos = info.chunkPos; // See whether we can remove the structure if (!Bounds.IsInside(ref pos)) { pendingStructureInfo.RemoveAt(i); } else { ++i; continue; } // Structure removed. We need to remove any associated world positions now for (int y = info.bounds.minY; y < info.bounds.maxY; y += Env.CHUNK_SIZE) { for (int z = info.bounds.minZ; z < info.bounds.maxZ; z += Env.CHUNK_SIZE) { for (int x = info.bounds.minX; x < info.bounds.maxX; x += Env.CHUNK_SIZE) { if (!pendingStructures.TryGetValue(new Vector3Int(x, y, z), out List <StructureContext> list) || list.Count <= 0) { continue; } // Remove any occurence of this structure from pending positions for (int j = 0; j < list.Count;) { if (list[j].id == info.id) { list.RemoveAt(j); } else { ++j; } } } } } } } }
public void RegisterPendingStructure(StructureInfo info, StructureContext context) { if (info == null || context == null) { return; } lock (pendingStructureMutex) { { bool alreadyThere = false; // Do not register the same thing twice for (int i = 0; i < pendingStructureInfo.Count; i++) { if (pendingStructureInfo[i].Equals(info)) { alreadyThere = true; break; } } if (!alreadyThere) { pendingStructureInfo.Add(info); } } if (pendingStructures.TryGetValue(context.chunkPos, out List <StructureContext> list)) { list.Add(context); } else { pendingStructures.Add(context.chunkPos, new List <StructureContext> { context }); } } { Chunk chunk; lock (chunks) { // Let the chunk know it needs an update if it exists chunk = GetChunk(ref context.chunkPos); } if (chunk != null) { chunk.needApplyStructure = true; } } }
public override void Build(Chunk chunk, int id, ref Vector3Int worldPos, TerrainLayer layer) { World world = chunk.World; int noise = Helpers.FastFloor(NoiseUtils.GetNoise(layer.Noise.Noise, worldPos.x, worldPos.y, worldPos.z, 1f, 3, 1f)); int leavesRange = MIN_CROWN_SIZE + noise; int leavesRange1 = leavesRange - 1; int trunkHeight = MIN_TRUNK_SIZE + noise; // Make the crown an ellipsoid flattened on the y axis float a2inv = 1.0f / (leavesRange * leavesRange); float b2inv = 1.0f / (leavesRange1 * leavesRange1); int x1 = worldPos.x - leavesRange; int x2 = worldPos.x + leavesRange; int y1 = worldPos.y + 1 + trunkHeight; int y2 = y1 + 1 + 2 * leavesRange1; int z1 = worldPos.z - leavesRange; int z2 = worldPos.z + leavesRange; int cx, cy, cz; int minX, minY, minZ; int maxX, maxY, maxZ; AABBInt bounds = new AABBInt(x1, worldPos.y, z1, x2, y2, z2); Vector3Int chunkPos = chunk.Pos; StructureInfo info = null; // Generate the crown Vector3Int posFrom = new Vector3Int(x1, y1, z1); Vector3Int posTo = new Vector3Int(x2, y2, z2); Vector3Int chunkPosFrom = Helpers.ContainingChunkPos(ref posFrom); Vector3Int chunkPosTo = Helpers.ContainingChunkPos(ref posTo); minY = Helpers.Mod(posFrom.y, Env.CHUNK_SIZE); for (cy = chunkPosFrom.y; cy <= chunkPosTo.y; cy += Env.CHUNK_SIZE, minY = 0) { maxY = System.Math.Min(posTo.y - cy, Env.CHUNK_SIZE_1); minZ = Helpers.Mod(posFrom.z, Env.CHUNK_SIZE); for (cz = chunkPosFrom.z; cz <= chunkPosTo.z; cz += Env.CHUNK_SIZE, minZ = 0) { maxZ = System.Math.Min(posTo.z - cz, Env.CHUNK_SIZE_1); minX = Helpers.Mod(posFrom.x, Env.CHUNK_SIZE); for (cx = chunkPosFrom.x; cx <= chunkPosTo.x; cx += Env.CHUNK_SIZE, minX = 0) { maxX = System.Math.Min(posTo.x - cx, Env.CHUNK_SIZE_1); int xOff = cx - worldPos.x; int yOff = cy - y1 - leavesRange1; int zOff = cz - worldPos.z; if (cx != chunk.Pos.x || cy != chunk.Pos.y || cz != chunk.Pos.z) { Vector3Int pos = new Vector3Int(cx, cy, cz); Vector3Int min = new Vector3Int(minX, minY, minZ); Vector3Int max = new Vector3Int(maxX, maxY, maxZ); if (info == null) { info = new StructureInfo(id, ref chunkPos, ref bounds); } world.RegisterPendingStructure(info, new StructureContextTreeCrown(id, ref pos, ref worldPos, ref min, ref max, noise)); continue; } // Actual crown construction ChunkBlocks blocks = chunk.Blocks; int index = Helpers.GetChunkIndex1DFrom3D(minX, minY, minZ); int yOffset = Env.CHUNK_SIZE_WITH_PADDING_POW_2 - (maxZ - minZ + 1) * Env.CHUNK_SIZE_WITH_PADDING; int zOffset = Env.CHUNK_SIZE_WITH_PADDING - (maxX - minX + 1); for (int y = minY; y <= maxY; ++y, index += yOffset) { for (int z = minZ; z <= maxZ; ++z, index += zOffset) { for (int x = minX; x <= maxX; ++x, ++index) { int xx = x + xOff; int yy = y + yOff; int zz = z + zOff; float _x = xx * xx * a2inv; float _y = yy * yy * b2inv; float _z = zz * zz * a2inv; if (_x + _y + _z <= 1.0f) { blocks.SetRaw(index, leaves); } } } } } } } // Generate the trunk posFrom = new Vector3Int(worldPos.x, worldPos.y, worldPos.z); posTo = new Vector3Int(worldPos.x, worldPos.y + trunkHeight, worldPos.z); chunkPosFrom = Helpers.ContainingChunkPos(ref posFrom); chunkPosTo = Helpers.ContainingChunkPos(ref posTo); cx = Helpers.MakeChunkCoordinate(worldPos.x); cz = Helpers.MakeChunkCoordinate(worldPos.z); int tx = Helpers.Mod(worldPos.x, Env.CHUNK_SIZE); int tz = Helpers.Mod(worldPos.z, Env.CHUNK_SIZE); minY = Helpers.Mod(posFrom.y, Env.CHUNK_SIZE); for (cy = chunkPosFrom.y; cy <= chunkPosTo.y; cy += Env.CHUNK_SIZE, minY = 0) { maxY = System.Math.Min(posTo.y - cy, Env.CHUNK_SIZE_1); if (cx != chunk.Pos.x || cy != chunk.Pos.y || cz != chunk.Pos.z) { Vector3Int pos = new Vector3Int(cx, cy, cz); Vector3Int min = new Vector3Int(tx, minY, tz); Vector3Int max = new Vector3Int(tx, maxY, tz); if (info == null) { info = new StructureInfo(id, ref chunkPos, ref bounds); } world.RegisterPendingStructure(info, new StructureContextTreeTrunk(id, ref pos, ref min, ref max)); continue; } // Actual trunk construction ChunkBlocks blocks = chunk.Blocks; int index = Helpers.GetChunkIndex1DFrom3D(tx, minY, tz); for (int y = minY; y <= maxY; ++y, index += Env.CHUNK_SIZE_WITH_PADDING_POW_2) { blocks.SetRaw(index, log); } } }