/// <summary>
        /// Get block map of Chunk with borders (borders used for texture blending)
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="border"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        private ChunkMaskBlock[,] GetChunkBlocks(Chunk chunk, int border, Dictionary <Vector2i, Chunk> map)
        {
            Chunk top, bottom, left, right, topleft, bottomleft, topright, bottomright;

            map.TryGetValue(chunk.Position + Vector2i.Forward, out top);
            map.TryGetValue(chunk.Position + Vector2i.Back, out bottom);
            map.TryGetValue(chunk.Position + Vector2i.Left, out left);
            map.TryGetValue(chunk.Position + Vector2i.Right, out right);
            map.TryGetValue(chunk.Position + Vector2i.Forward + Vector2i.Left, out topleft);
            map.TryGetValue(chunk.Position + Vector2i.Back + Vector2i.Left, out bottomleft);
            map.TryGetValue(chunk.Position + Vector2i.Forward + Vector2i.Right, out topright);
            map.TryGetValue(chunk.Position + Vector2i.Back + Vector2i.Right, out bottomright);

            var bc     = chunk.BlocksCount;
            var blocks = new ChunkMaskBlock[chunk.BlocksCount + 2 * border, chunk.BlocksCount + 2 * border];

            CopyBlocks(chunk, blocks, new Bounds2i(Vector2i.Zero, Vector2i.One * (bc - 1)), Vector2i.One * border);

            if (border > 0)
            {
                if (bottomleft != null)
                {
                    CopyBlocks(bottomleft, blocks, new Bounds2i(Vector2i.One * (bc - border), border, border), Vector2i.Zero);
                }
                if (bottom != null)
                {
                    CopyBlocks(bottom, blocks, new Bounds2i(new Vector2i(0, bc - border), bc, border), new Vector2i(border, 0));
                }
                if (bottomright != null)
                {
                    CopyBlocks(bottomright, blocks, new Bounds2i(new Vector2i(0, bc - border), border, border), new Vector2i(bc + border, 0));
                }
                if (left != null)
                {
                    CopyBlocks(left, blocks, new Bounds2i(new Vector2i(bc - border, 0), border, bc), new Vector2i(0, border));
                }
                if (right != null)
                {
                    CopyBlocks(right, blocks, new Bounds2i(new Vector2i(0, 0), border, bc), new Vector2i(bc + border, border));
                }
                if (topleft != null)
                {
                    CopyBlocks(topleft, blocks, new Bounds2i(new Vector2i(bc - border, 0), border, border), new Vector2i(0, bc + border));
                }
                if (top != null)
                {
                    CopyBlocks(top, blocks, new Bounds2i(new Vector2i(0, 0), bc, border), new Vector2i(border, bc + border));
                }
                if (topright != null)
                {
                    CopyBlocks(topright, blocks, new Bounds2i(Vector2i.Zero, border, border), new Vector2i(bc + border));
                }
            }

            return(blocks);
        }
        private TerrainMap PrepareGeometryMap(ChunkMaskBlock[,] blocks, int border)
        {
            var result = new Color[blocks.Length];
            for (int z = 0; z <= blocks.GetUpperBound(1); z++)
                for (int x = 0; x <= blocks.GetUpperBound(0); x++)
                {
                    var normal = blocks[x, z].Normal;
                    result[x + z * blocks.GetLength(1)].r = normal.x / 2 + 0.5f;
                    result[x + z * blocks.GetLength(1)].g = normal.y / 2 + 0.5f;
                    result[x + z * blocks.GetLength(1)].b = normal.z / 2 + 0.5f;
                    result[x + z * blocks.GetLength(1)].a = Vector3.Angle(normal, Vector3.up)/90f;
                }

            var resultTexture = new Texture2D(blocks.GetLength(0), blocks.GetLength(1), TextureFormat.RGBA32, false);
            resultTexture.wrapMode = TextureWrapMode.Clamp;
            resultTexture.SetPixels(result);
            resultTexture.Apply(false, true);
            return new TerrainMap() {Map = resultTexture, Border = border};
        }
        private Texture2D PrepareBlockTypeMask(ChunkMaskBlock[,] blocks)
        {
            var result = new Color[blocks.Length];
            for (int z = 0; z <= blocks.GetUpperBound(1); z++)
                for (int x = 0; x <= blocks.GetUpperBound(0); x++)
                {
                    var blockType = blocks[x, z].Block;
                    if (blockType == BlockType.Grass)
                        result[x + z * blocks.GetLength(1)] = new Color(0, 1, 0, 0);
                    else if (blockType == BlockType.Sand)
                        result[x + z * blocks.GetLength(1)] = new Color(1, 0, 0, 0);
                    else if (blockType == BlockType.Water)
                        result[x + z * blocks.GetLength(1)] = new Color(0, 0, 1, 0);
                    else if (blockType == BlockType.Snow)
                        result[x + z * blocks.GetLength(1)] = new Color(0, 0, 0, 1);
                    //Stone - no color at all
                }

            var resultTexture = new Texture2D(blocks.GetLength(0), blocks.GetLength(1), TextureFormat.RGBA32, false);
            resultTexture.wrapMode = TextureWrapMode.Clamp;
            resultTexture.SetPixels(result);
            resultTexture.Apply(false, true);
            return resultTexture;
        }
 private void CopyBlocks(Chunk src, ChunkMaskBlock[,] dest, Bounds2i srcBounds, Vector2i destPosition)
 {
     for (int z = srcBounds.Min.Z; z <= srcBounds.Max.Z; z++)
     {
         var destPosZ = destPosition.Z + (z - srcBounds.Min.Z);
         for (int x = srcBounds.Min.X; x <= srcBounds.Max.X; x++)
         {
             var destPosX = destPosition.X + (x - srcBounds.Min.X);
             dest[destPosX, destPosZ].Block = src.BlockType[x, z];
             dest[destPosX, destPosZ].Normal = src.NormalMap[x, z];
         }
     }
 }
        private ChunkMaskBlock[,] CalculateBlockMap(Chunk chunk, int border, Dictionary<Vector2i, Chunk> map)
        {
            Chunk top, bottom, left, right, topleft, bottomleft, topright, bottomright;
            map.TryGetValue(chunk.Position + Vector2i.Forward, out top);
            map.TryGetValue(chunk.Position + Vector2i.Back, out bottom);
            map.TryGetValue(chunk.Position + Vector2i.Left, out left);
            map.TryGetValue(chunk.Position + Vector2i.Right, out right);
            map.TryGetValue(chunk.Position + Vector2i.Forward + Vector2i.Left, out topleft);
            map.TryGetValue(chunk.Position + Vector2i.Back + Vector2i.Left, out bottomleft);
            map.TryGetValue(chunk.Position + Vector2i.Forward + Vector2i.Right, out topright);
            map.TryGetValue(chunk.Position + Vector2i.Back + Vector2i.Right, out bottomright);

            var bc = chunk.BlocksCount;
            var blocks = new ChunkMaskBlock[chunk.BlocksCount + 2*border, chunk.BlocksCount + 2*border];

            CopyBlocks(chunk, blocks, new Bounds2i(Vector2i.Zero, Vector2i.One*(bc - 1)), Vector2i.One*border);

            if (border > 0)
            {
                if(bottomleft != null)
                    CopyBlocks(bottomleft, blocks, new Bounds2i(Vector2i.One * (bc - border), border, border), Vector2i.Zero);
                if (bottom != null)
                    CopyBlocks(bottom, blocks, new Bounds2i(new Vector2i(0, bc - border), bc, border), new Vector2i(border, 0));
                if (bottomright != null)
                    CopyBlocks(bottomright, blocks, new Bounds2i(new Vector2i(0, bc - border), border, border), new Vector2i(bc + border, 0));
                if (left != null)
                    CopyBlocks(left, blocks, new Bounds2i(new Vector2i(bc - border, 0), border, bc), new Vector2i(0, border));
                if (right != null)
                    CopyBlocks(right, blocks, new Bounds2i(new Vector2i(0, 0), border, bc), new Vector2i(bc+border, border));
                if (topleft != null)
                    CopyBlocks(topleft, blocks, new Bounds2i(new Vector2i(bc - border, 0), border, border), new Vector2i(0, bc + border));
                if (top != null)
                    CopyBlocks(top, blocks, new Bounds2i(new Vector2i(0, 0), bc, border), new Vector2i(border, bc+border));
                if (topright != null)
                    CopyBlocks(topright, blocks, new Bounds2i(Vector2i.Zero, border, border), new Vector2i(bc + border));
            }

            return blocks;
        }