public void GenerateCaves(VoxelChunk chunk)
        {
            Vector3      origin     = chunk.Origin;
            int          chunkSizeX = chunk.SizeX;
            int          chunkSizeY = chunk.SizeY;
            int          chunkSizeZ = chunk.SizeZ;
            BiomeData    biome      = BiomeLibrary.Biomes[Overworld.Biome.Cave];
            List <Voxel> neighbors  = new List <Voxel>();
            Voxel        vUnder     = chunk.MakeVoxel(0, 0, 0);

            for (int x = 0; x < chunkSizeX; x++)
            {
                for (int z = 0; z < chunkSizeZ; z++)
                {
                    int h = chunk.GetFilledVoxelGridHeightAt(x, chunk.SizeY - 1, z);
                    for (int i = 0; i < CaveLevels.Count; i++)
                    {
                        int y = CaveLevels[i];
                        if (y <= 0 || y >= h - 1)
                        {
                            continue;
                        }
                        Vector3 vec       = new Vector3(x, y, z) + chunk.Origin;
                        double  caveNoise = CaveNoise.GetValue((x + origin.X) * CaveNoiseScale * CaveFrequencies[i],
                                                               (y + origin.Y) * CaveNoiseScale * 3.0f, (z + origin.Z) * CaveNoiseScale * CaveFrequencies[i]);

                        double heightnoise = NoiseGenerator.Noise((x + origin.X) * NoiseScale * CaveFrequencies[i],
                                                                  (y + origin.Y) * NoiseScale * 3.0f, (z + origin.Z) * NoiseScale * CaveFrequencies[i]);

                        int caveHeight = Math.Min(Math.Max((int)(heightnoise * 5), 1), 3);

                        if (caveNoise > CaveSize)
                        {
                            bool waterFound = false;
                            for (int dy = 0; dy < caveHeight; dy++)
                            {
                                int index = chunk.Data.IndexAt(x, y - dy, z);
                                chunk.GetNeighborsManhattan(x, y - dy, z, neighbors);

                                if (neighbors.Any(v => v.WaterLevel > 0))
                                {
                                    waterFound = true;
                                }

                                if (waterFound)
                                {
                                    break;
                                }

                                chunk.Data.Types[index] = 0;
                            }

                            if (!waterFound && caveNoise > CaveSize * 1.8f && y - caveHeight > 0)
                            {
                                int indexunder = chunk.Data.IndexAt(x, y - caveHeight, z);
                                chunk.Data.Types[indexunder]      = (byte)VoxelLibrary.GetVoxelType(biome.GrassVoxel).ID;
                                chunk.Data.Health[indexunder]     = (byte)VoxelLibrary.GetVoxelType(biome.GrassVoxel).StartingHealth;
                                chunk.Data.IsExplored[indexunder] = false;
                                foreach (VegetationData veg in biome.Vegetation)
                                {
                                    if (!MathFunctions.RandEvent(veg.SpawnProbability))
                                    {
                                        continue;
                                    }

                                    if (NoiseGenerator.Noise(vec.X / veg.ClumpSize, veg.NoiseOffset, vec.Y / veg.ClumpSize) < veg.ClumpThreshold)
                                    {
                                        continue;
                                    }


                                    vUnder.GridPosition = new Vector3(x, y - 1, z);
                                    if (!vUnder.IsEmpty && vUnder.TypeName == biome.GrassVoxel)
                                    {
                                        vUnder.Type = VoxelLibrary.GetVoxelType(biome.SoilVoxel);
                                        float offset = veg.VerticalOffset;
                                        if (vUnder.RampType != RampType.None)
                                        {
                                            offset -= 0.25f;
                                        }
                                        float         treeSize = MathFunctions.Rand() * veg.SizeVariance + veg.MeanSize;
                                        GameComponent entity   = EntityFactory.CreateEntity <GameComponent>(veg.Name, chunk.Origin + new Vector3(x, y, z) + new Vector3(0, treeSize * offset, 0), Blackboard.Create("Scale", treeSize));
                                        entity.GetRootComponent().SetActiveRecursive(false);
                                        entity.GetRootComponent().SetVisibleRecursive(false);
                                        if (GameSettings.Default.FogofWar)
                                        {
                                            ExploredListener listener = new ExploredListener(
                                                PlayState.ComponentManager, entity, PlayState.ChunkManager, vUnder);
                                        }
                                    }
                                }
                            }

                            foreach (FaunaData animal in biome.Fauna)
                            {
                                if (y <= 0 || !(PlayState.Random.NextDouble() < animal.SpawnProbability))
                                {
                                    continue;
                                }


                                var entity = EntityFactory.CreateEntity <GameComponent>(animal.Name, chunk.Origin + new Vector3(x, y, z) + Vector3.Up * 1.0f);
                                entity.GetRootComponent().SetActiveRecursive(false);
                                entity.GetRootComponent().SetVisibleRecursive(false);

                                if (GameSettings.Default.FogofWar)
                                {
                                    ExploredListener listener = new ExploredListener(PlayState.ComponentManager, entity,
                                                                                     PlayState.ChunkManager, chunk.MakeVoxel(x, y, z));
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }