private void GenerateTrees()
        {
            float simplex1 = noise.GetSimplex(chunkPosX * .3f, chunkPosZ * .3f);
            float simplex2 = noise.GetSimplex(chunkPosX * 2f, chunkPosZ * 2f);
            float value    = (simplex1 + simplex2) / 2;

            if (value > 0)
            {
                int minpos = 4;
                int maxpos = ChunkSizeXZ - 5;

                NativeList <int2> treePositions = new NativeList <int2>(MaxTreesPerChunk, Allocator.Temp);
                for (int i = 0; i < MaxTreesPerChunk; i++)
                {
                    int x = random.NextInt(minpos, maxpos);
                    int z = random.NextInt(minpos, maxpos);

                    BiomeType biomeType = biomeTypes[Utils.BlockPosition2DtoIndex(x, z)];
                    // TODO: bool CanPlaceTree(BiomeType type)
                    if (biomeType != BiomeType.FOREST && biomeType != BiomeType.PLAINS)
                    {
                        continue;
                    }

                    bool doContinue = false;
                    for (int j = 0; j < treePositions.Length; j++)
                    {
                        int2 pos = treePositions[j];
                        if (math.sqrt((pos.x - x) * (pos.x - x) + (pos.y - x) * (pos.y - x)) < MinDistanceBetweenTrees)
                        {
                            doContinue = true;
                            break;
                        }
                    }
                    if (doContinue)
                    {
                        continue;
                    }

                    int y = ChunkSizeY - TreeHeightRange.x;
                    if (blockData[Utils.BlockPosition3DtoIndex(x, y, z)] != BlockType.AIR)
                    {
                        continue;       // continue if maxTerrainHeigth - minTreeHeigth hits ground
                    }
                    // find ground position
                    while (y > 0 && blockData[Utils.BlockPosition3DtoIndex(x, y, z)] == BlockType.AIR)
                    {
                        y--;
                    }

                    BlockType groundBlock = blockData[Utils.BlockPosition3DtoIndex(x, y, z)];
                    if (!WorldData.CanPlaceTree(groundBlock))
                    {
                        continue;       // continue if cant place tree on ground block
                    }
                    // add position to position list
                    treePositions.Add(new int2(x, z));

                    TreeSettings treeSettings = Trees.OakTree;
                    // place logs
                    int treeHeight = random.NextInt(treeSettings.TreeHeightRange.x, treeSettings.TreeHeightRange.y);
                    for (int j = 0; j < treeHeight; j++)
                    {
                        if (y + j < ChunkSizeY)
                        {
                            blockData[Utils.BlockPosition3DtoIndex(x, y + j, z)] = BlockType.OAK_LOG;
                        }
                    }

                    int treeTop = y + treeHeight - 1;
                    int index, xrange, zrange;

                    // <0, 1>
                    xrange = 1;
                    zrange = 1;
                    for (int _y = treeTop; _y <= treeTop + 1; _y++)
                    {
                        for (int _x = -xrange; _x <= xrange; _x++)
                        {
                            for (int _z = -zrange; _z <= zrange; _z++)
                            {
                                index = Utils.BlockPosition3DtoIndex(x + _x, _y, z + _z);
                                if (blockData[index] == BlockType.AIR)
                                {
                                    blockData[index] = BlockType.OAK_LEAVES;
                                }
                            }
                        }

                        // x- z-
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x - xrange, _y, z - zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x- z+
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x - xrange, _y, z + zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x+ z-
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x + xrange, _y, z - zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x+ z+
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x + xrange, _y, z + zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                    }

                    // <-1, -2>
                    xrange = 2;
                    zrange = 2;
                    for (int _y = treeTop - 2; _y <= treeTop - 1; _y++)
                    {
                        for (int _x = -xrange; _x <= xrange; _x++)
                        {
                            for (int _z = -zrange; _z <= zrange; _z++)
                            {
                                index = Utils.BlockPosition3DtoIndex(x + _x, _y, z + _z);
                                if (blockData[index] == BlockType.AIR)
                                {
                                    blockData[index] = BlockType.OAK_LEAVES;
                                }
                            }
                        }

                        // x- z-
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x - xrange, _y, z - zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x- z+
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x - xrange, _y, z + zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x+ z-
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x + xrange, _y, z - zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x+ z+
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x + xrange, _y, z + zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                    }

                    // <-3, -3>
                    xrange = 1;
                    zrange = 1;
                    for (int _y = treeTop - 3; _y <= treeTop - 3; _y++)
                    {
                        for (int _x = -xrange; _x <= xrange; _x++)
                        {
                            for (int _z = -zrange; _z <= zrange; _z++)
                            {
                                index = Utils.BlockPosition3DtoIndex(x + _x, _y, z + _z);
                                if (blockData[index] == BlockType.AIR)
                                {
                                    blockData[index] = BlockType.OAK_LEAVES;
                                }
                            }
                        }

                        // x- z-
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x - xrange, _y, z - zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x- z+
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x - xrange, _y, z + zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x+ z-
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x + xrange, _y, z - zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                        // x+ z+
                        if (random.NextBool() && AreTypesEqual(BlockType.OAK_LEAVES, x + xrange, _y, z + zrange, out index))
                        {
                            blockData[index] = BlockType.AIR;
                        }
                    }
                }
            }
        }