コード例 #1
0
ファイル: ChunkCache.cs プロジェクト: HaKDMoDz/4DBlockEngine
 public ChunkCache(Game game)
     : base(game)
 {
     m_logger = MainEngine.GetEngineInstance().GetLogger("ChunkCache");
     Debug.Assert(game != null);
     var graphicsDevice = game.GraphicsDevice;
     Debug.Assert(ViewRange < CacheRange);
     Debug.Assert(graphicsDevice != null);
     Blocks = new Block[CacheSizeInBlocks * CacheSizeInBlocks * CacheSizeInBlocks];
     m_lightingEngine = new CellularLighting<Block>(Blocks, BlockIndexByWorldPosition, Chunk.SizeInBlocks, GetChunkByWorldPosition);
     m_vertexBuilder = new VertexBuilder<Block>(Blocks, BlockIndexByWorldPosition, graphicsDevice);
     m_chunkStorage = new SparseArray3D<Chunk>(CacheRange * 2 + 1, CacheRange * 2 + 1);
     m_cacheCenterPosition = new Vector4();
     m_eventSinkImpl = new EventSinkImpl ();
     m_eventSinkImpl.AddHandler<Vector3Args>(EventConstants.PlayerPositionUpdated, OnUpdateCachePosition);
     m_startUpState = StartUpState.NotStarted;
     m_processingQueue = new Queue<Chunk>();
     m_EditQueue = new ConcurrentQueue<WorldEdit>();
     #if DEBUG
     StateStatistics = new Dictionary<ChunkState, int> // init. the debug stastics.
                                {
                                    {ChunkState.AwaitingGenerate, 0},
                                    {ChunkState.Generating, 0},
                                    {ChunkState.AwaitingLighting, 0},
                                    {ChunkState.Lighting, 0},
                                    {ChunkState.AwaitingBuild, 0},
                                    {ChunkState.Building, 0},
                                    {ChunkState.Ready, 0},
                                    {ChunkState.AwaitingRemoval, 0},
                                };
     #endif
 }
コード例 #2
0
ファイル: Page.cs プロジェクト: HaKDMoDz/4DBlockEngine
 public Page(int x, int y, int z, string pageId, Block[] data)
 {
     X = x;
     Y = y;
     Z = z;
     PageId = pageId;
     Data = data;
 }
コード例 #3
0
 public SortedList<Interval, Block> ConvertArrayToIntervalTree(Block[] blocks, out float compressionRatio, CompressionFlag flag = CompressionFlag.None)
 {
     var scanDirection = ScanDirection.Xyz;
     CompressionMode compressionMode;
     if (flag == CompressionFlag.None)
     {
         float hilbertCompressionRatio;
         float linearCompressionRatio;
         EvaluateHilbertTree(blocks, out hilbertCompressionRatio);
         EvaluateLinearTree(blocks, out linearCompressionRatio, out scanDirection);
         compressionMode = hilbertCompressionRatio > linearCompressionRatio ? CompressionMode.Hilbert : CompressionMode.Linear;
     }
     else
     {
         switch (flag)
         {
             case CompressionFlag.LinearXyz:
                 compressionMode = CompressionMode.Linear;
                 scanDirection = ScanDirection.Xyz;
                 break;
             case CompressionFlag.LinearXzy:
                 compressionMode = CompressionMode.Linear;
                 scanDirection = ScanDirection.Xzy;
                 break;
             case CompressionFlag.LinearYxz:
                 compressionMode = CompressionMode.Linear;
                 scanDirection = ScanDirection.Yxz;
                 break;
             case CompressionFlag.LinearYzx:
                 compressionMode = CompressionMode.Linear;
                 scanDirection = ScanDirection.Yzx;
                 break;
             case CompressionFlag.LinearZxy:
                 compressionMode = CompressionMode.Linear;
                 scanDirection = ScanDirection.Zxy;
                 break;
             case CompressionFlag.LinearZyx:
                 compressionMode = CompressionMode.Linear;
                 scanDirection = ScanDirection.Zyx;
                 break;
             case CompressionFlag.Hilbert:
                 compressionMode = CompressionMode.Hilbert;
                 break;
             default:
                 throw new ArgumentOutOfRangeException("flag");
         }
     }
     if (compressionMode == CompressionMode.Hilbert)
     {
         //Console.Out.WriteLine(CompressionMode.Hilbert.ToString());
         return ConvertArrayToIntervalTreeHilbert(blocks, out compressionRatio);
     }
     else
     {
         //Console.Out.WriteLine(scanDirection.ToString());
         return ConvertArrayToIntervalTreeLinear(blocks, scanDirection, out compressionRatio);
     }
 }
コード例 #4
0
 //TODO : make a physics thread and put this on it
 public FluidSimulation(MappingFunction mappingFunction, Block[] blocks)
 {
     m_mappingFunction = mappingFunction;
     m_blocks = blocks;
     m_containers = new List<FluidContainer>();
     m_solidCell = new FluidCell(FluidCell.CellType.Solid);
     m_addQueue = new Queue<List<FluidCell>>();
     m_cellAccumulator = new List<FluidCell>();
 }
コード例 #5
0
 public FluidContainer(MappingFunction mappingFunction, Block[] blocks, FluidCell solidCellReference)
 {
     m_mappingFunction = mappingFunction;
     m_blocks = blocks;
     m_solidCellReference = solidCellReference;
     Update = true;
     Alive = true;
     m_min = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue);
     m_max = new Vector3Int(-int.MaxValue, -int.MaxValue, -int.MaxValue);
     Cells = new List<FluidCell>();
     CellDictionary = new Dictionary<long, FluidCell>();
     Updated = new List<FluidCell>();
 }
コード例 #6
0
ファイル: Player.cs プロジェクト: HaKDMoDz/4DBlockEngine
 public Player(Block[] blocks, MappingFunctionVector3 mappingFunction)
 {
     FlyingEnabled = true;
     m_blocks = blocks;
     m_mappingFunction = mappingFunction;
     Equipable = new Shovel();
     m_eventSinkImpl = new EventSinkImpl();
     m_eventSinkImpl.AddHandler<Vector2Args>(EventConstants.MousePositionUpdated, OnMouseUpdated);
     m_eventSinkImpl.AddHandler<KeyArgs>(EventConstants.KeyDown, OnKeyDown);
     m_eventSinkImpl.AddHandler<KeyArgs>(EventConstants.KeyUp, OnKeyUp);
     m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.LeftMouseDown, OnLeftMouseDown);
     m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.LeftMouseUp, OnLeftMouseUp);
     m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.RightMouseDown, OnRightMouseDown);
     m_eventSinkImpl.AddHandler<MouseButtonArgs>(EventConstants.RightMouseUp, OnRightMouseUp);
     EventsFired = new[]
     {
         EventConstants.PlayerPositionUpdated,
         EventConstants.ViewUpdated
     };
     m_eventSourceImpl = new EventSource(EventsFired, true);
 }
コード例 #7
0
ファイル: Simulation.cs プロジェクト: HaKDMoDz/4DBlockEngine
 public void AddBlockAt(int x, int y, int z, ref Block block)
 {
     m_chunkCache.AddBlock(x, y, z, ref block);
 }
コード例 #8
0
        public void GenerateDataForChunk(int chunkX, int chunkY, int chunkZ, int chunkSize, Block[] blocks, MappingFunction mappingFunction)
	    {
           for (var x  = 0; x < chunkSize; ++x) 
		   {
                var cX = chunkX + x;
			    for (var z = 0; z < chunkSize; ++z) 
			    {
                    var cZ = chunkZ + z;
			        var groundLevel = GetHeight(cX, cZ);
					var detailNoise =  m_detail2.FractalBrownianMotion(cX, cZ, 64, 0, 8);
                    var overhangStart = m_sealevel + MathHelper.Clamp(detailNoise * 4, -1, 1) * 2;
                    var biome = m_biomeGenerator.GetRegionGenerator(cX, cZ);
                    var province = m_provinceGenerator.GetRegionGenerator(cX, cZ);

			        var data = m_populator.CalculateNearestSamplePosition(cX, cZ);
			        /*var color = (ushort)data.Id;*/
			        var riverData = m_riverGenerator.GetRiverData(cX, cZ);

                    if (riverData != null)
			        {
			           /* if (groundLevel < riverData.Position.Y)
			            {
			                Console.Out.WriteLine(x + ", " + z + ": " + (riverData.Position.Y - groundLevel));
			            }*/
			            groundLevel = riverData.Position.Y;
			        }

					for (var y = chunkSize - 1; y >= 0 ; --y)
					{
                        var cY = chunkY + y;
                        Block block;

                        if (cY > groundLevel + 10)
					    {
                            block = new Block(Block.None) {LightSun = CellularLighting<Block>.MaxSun};
					    }
					    else if (cY > groundLevel)
					    {
                            block = new Block(Block.None) { LightSun = CellularLighting<Block>.MinLight };
					    }
                        else if (cY >= groundLevel - m_biomeThickness)
					    {
					        if (cY > overhangStart)
					        {
					            var density = (MathHelper.Clamp(m_volume.FractalBrownianMotion(cX, cY, cZ, 64, 0, 4)*3, -1, 1) + 1)*0.5f;
					            block = density > 0.125 ? biome.Apply((int) groundLevel, cX, cY, cZ) : Block.Empty;
					        }
					        else
					        {
					            block = biome.Apply((int) groundLevel, cX, cY, cZ);
					        }
					    }
					    else
					    {
					        block = province.Apply((int)groundLevel - m_biomeThickness, cX, cY, cZ);
					    }
					  //  var tint = (int) ((detailNoise * 5 + 10));
                     //   block.Color = (ushort)((tint << 8) | (tint << 4) | tint);
					    if (riverData != null && cY <= groundLevel && cY >= groundLevel - 1)
					    {
					        block = new Block(BlockDictionary.Instance.GetBlockIdForName("Water"));
					    }

                        blocks[mappingFunction(cX, cY, cZ)] = block;
					}
                    
                    for (var y = 0; y < chunkSize && chunkY + y < m_sealevel; ++y)
					{		
                        var cY = chunkY + y;
					    var blockIndex = mappingFunction(cX, cY, cZ);
					    if (!blocks[blockIndex].Exists)
					        blocks[blockIndex] = new Block(BlockDictionary.Instance.GetBlockIdForName("Water"));
					}


                    //TODO : redesign populator to be compatible with page loading
			       /* if (Math.Abs(data.Delta.X - cX) < 0.01 && Math.Abs(data.Delta.Y - cZ) < 0.01)
			        {
			            m_populator.PopulateTree(cX, cZ, (int)groundLevel, blocks, mappingFunction);
			        }*/
				}
		    }
            //TODO : query if chunk has all neighbors loaded so we can run our erosion step

            //TODO : after eroding generate structures
	    }
コード例 #9
0
ファイル: Chunk.cs プロジェクト: HaKDMoDz/4DBlockEngine
        public Chunk(Vector3Int chunkCachePosition, Block[] blocks, MappingFunction mappingFunction, GetNeighborChunk getNeighborChunk)
        {
            ChunkState = ChunkState.AwaitingGenerate; // set initial state to awaiting generation.
            ChunkCachePosition = chunkCachePosition; // set the relative position.
            m_blocks = blocks;
            m_mappingFunction = mappingFunction;
            m_getNeighborChunk = getNeighborChunk;

            // calculate the real world position.
            Position = new Vector3Int(ChunkCachePosition.X * SizeInBlocks,
                                           ChunkCachePosition.Y * SizeInBlocks,
                                           ChunkCachePosition.Z * SizeInBlocks);

            // calculate bounding-box.
            BoundingBox = new BoundingBox(new Vector3(Position.X, Position.Y, Position.Z),
                                          new Vector3(Position.X + SizeInBlocks, Position.Y + SizeInBlocks,
                                                      Position.Z + SizeInBlocks));
            #if DEBUG
            MainEngine.GetEngineInstance().DebugOnlyDebugManager.RegisterInGameDebuggable(this);
            #endif
        }
コード例 #10
0
ファイル: PageManager.cs プロジェクト: HaKDMoDz/4DBlockEngine
		public Page DecompressPage(string pageId)
		{
		    var buffer = CheckOutBuffer();
			Page page;
            using (var fileStream = new FileStream(Path.Combine(m_dataDirectory, pageId + ".page"), FileMode.Open))
			{
				using(var decompressor = new GZipStream(fileStream, CompressionMode.Decompress))
				{
				    decompressor.Read(buffer, 0, BufferSize);
                    var pageX = GetInt(buffer, 0);
                    var pageY = GetInt(buffer, 4);
                    var pageZ = GetInt(buffer, 8);
                    page = new Page(pageX, pageY, pageZ, pageId);
                    for (var curveIndex = 0; curveIndex < BlockCount; ++curveIndex)
                    {
                        var offset = HeaderSize + curveIndex * 2;
                        var block = new Block(GetShort(buffer, offset));
                        offset = HeaderSize + BlockCount * 2 + curveIndex;
                        block.LightSun = buffer[offset];
                        offset = HeaderSize + BlockCount * 3 + curveIndex;
                        block.LightRed = buffer[offset];
                        offset = HeaderSize + BlockCount * 4 + curveIndex;
                        block.LightGreen = buffer[offset];
                        offset = HeaderSize + BlockCount * 5 + curveIndex;
                        block.LightBlue = buffer[offset];
                        offset = HeaderSize + BlockCount * 6 + curveIndex * 2;
                        block.Color = GetShort(buffer, offset);
                        page.Data[m_hilbertCurve[curveIndex]] = block;
                    }
				}
            }
		    CheckInBuffer(buffer);
            return page;
		}
コード例 #11
0
 private void EvaluateLinearTree(Block[] blocks, out float compressionRatio, out ScanDirection optimalDirection)
 {
     var count = m_chunkSize * m_chunkSize * m_chunkSize;
     compressionRatio = 0;
     optimalDirection = ScanDirection.Xyz;
     foreach (ScanDirection scanDirection in Enum.GetValues(typeof(ScanDirection)))
     {
         var nodesRemoved = count;
         Block current = Block.Empty;
         Vector3Int workCoords = new Vector3Int();
         for (var i = 0; i < count; ++i)
         {
             var block = blocks[GetNextIndex(scanDirection, m_chunkSize, ref workCoords)];
             if (block == current)
             {
                 continue;
             }
             current = block;
             --nodesRemoved;
         }
         var currentRatio = nodesRemoved / (float)count;
         if (currentRatio <= compressionRatio)
         {
             continue;
         }
         compressionRatio = currentRatio;
         optimalDirection = scanDirection;
         //this means we are mostly air and can just exit out now!
         if (currentRatio > 0.97f)
         {
             break;
         }
     }
 }
コード例 #12
0
 private void EvaluateHilbertTree(Block[] blocks, out float compressionRatio)
 {
     var nodesRemoved = m_hilbertCurve.Length;
     var current = Block.Empty;
     foreach (var index in m_hilbertCurve)
     {
         var block = blocks[index];
         if (block == current)
         {
             continue;
         }
         current = block;
         --nodesRemoved;
     }
     compressionRatio = nodesRemoved / (float)m_hilbertCurve.Length;
 }
コード例 #13
0
 private SortedList<Interval, Block> ConvertArrayToIntervalTreeLinear(Block[] blocks, ScanDirection optimalDirection, out float compressionRatio)
 {
     var count = m_chunkSize * m_chunkSize * m_chunkSize;
     var nodesRemoved = count;
     var intervals = new SortedList<Interval, Block>();
     var min = 0;
     var max = 0;
     var current = Block.Empty;
     var started = false;
     var workCoords = new Vector3Int();
     for (var i = 0; i < count; ++i)
     {
         var block = blocks[GetNextIndex(optimalDirection, m_chunkSize, ref workCoords)];
         if (!(started && block == current))
         {
             if (started)
             {
                 intervals.Add(new Interval((ushort)min, (ushort)max), current);
             }
             current = block;
             min = i;
             started = true;
             --nodesRemoved;
         }
         max = i;
     }
     intervals.Add(new Interval((ushort)min, (ushort)max), current);
     compressionRatio = nodesRemoved / (float)count;
     return intervals;
 }
コード例 #14
0
 private SortedList<Interval, Block> ConvertArrayToIntervalTreeHilbert(Block[] blocks, out float compressionRatio)
 {
     var nodesRemoved = m_hilbertCurve.Length;
     var min = 0;
     var max = 0;
     var current = Block.Empty;
     var started = false;
     var intervals = new SortedList<Interval, Block>();
     for (var i = 0; i < m_hilbertCurve.Length; ++i)
     {
         var block = blocks[m_hilbertCurve[i]];
         if (!(started && block == current))
         {
             if (started)
             {
                 intervals.Add(new Interval((ushort)min, (ushort)max), current);
             }
             current = block;
             min = i;
             started = true;
             --nodesRemoved;
         }
         max = i;
     }
     intervals.Add(new Interval((ushort)min, (ushort)max), current);
     compressionRatio = nodesRemoved / (float)m_hilbertCurve.Length;
     return intervals;
 }
コード例 #15
0
ファイル: PageManager.cs プロジェクト: HaKDMoDz/4DBlockEngine
	    public void SaveChunk(Vector3Int position, Block[] blocks, MappingFunction mappingFunction, Action<bool> callback)
        {
            var pagePositionX = position.X >> 6;
            var pagePositionY = position.Y >> 6;
            var pagePositionZ = position.Z >> 6;
	        var pageId = CreatePageId(pagePositionX, pagePositionY, pagePositionZ);
	        if (m_pagesPendingWrite.Contains(pageId))
	        {
                if (callback != null)
                {
                    callback(false);
                }
	        }
	        else
	        {
	            Page page;
	            if (m_pageCache.ContainsPage(pageId))
	            {
                    page = m_pageCache.GetPage(pageId);
                    CopyCunkToPage(page, position, blocks, mappingFunction);
	            }
                else if (m_directory.Pages.Contains(pageId))
                {
                    page = DecompressPage(pageId);
                    CopyCunkToPage(page, position, blocks, mappingFunction);
                    m_pageCache.InsertPage(page);
                }
	            else
	            {
                    page = new Page(position.X, position.Y, position.Z, pageId);
                    for (var x = 0; x < Page.PageSizeInBlocks; x++)
                    {
                        for (var y = 0; y < Page.PageSizeInBlocks; y++)
                        {
                            for (var z = 0; z < Page.PageSizeInBlocks; z++)
                            {
                                page.Data[Page.BlockIndexFromRelativePosition(x, y, z)] =
                                    blocks[mappingFunction(position.X + x, position.Y + y, position.Z + z)];
                            }
                        }
                    }
                    m_pageCache.InsertPage(page);
                    m_directory.Pages.Add(pageId);
                   // m_jsonWriter.Write("SaveDirectory", m_directory);
	            }
	            m_pagesPendingWrite.Add(pageId);
	            Task.Run(() =>
	            {
	                CompressPage(page);
	                if (callback != null)
	                {
	                    callback(true);
	                }
	                m_pagesPendingWrite.Remove(page.PageId);
	            });
	        }
	    }
コード例 #16
0
ファイル: PageManager.cs プロジェクト: HaKDMoDz/4DBlockEngine
        private static void CopyCunkToPage(Page page, Vector3Int position, Block[] blocks, MappingFunction mappingFunction)
        {
            var originX = MathUtilities.Modulo(position.X, Page.PageSizeInBlocks);
            var originY = MathUtilities.Modulo(position.Y, Page.PageSizeInBlocks);
            var originZ = MathUtilities.Modulo(position.Z, Page.PageSizeInBlocks);
            for (var x = originX; x < originX + Chunk.SizeInBlocks; x++)
            {
                for (var y = originY; y < originY + Chunk.SizeInBlocks; y++)
                {
                    for (var z = originZ; z < originZ + Chunk.SizeInBlocks; z++)
                    {
                        page.Data[Page.BlockIndexFromRelativePosition(x, y, z)] =
                            blocks[mappingFunction(position.X + x, position.Y + y, position.Z + z)];
                    }
                }
            }
	    }
コード例 #17
0
ファイル: Block.cs プロジェクト: HaKDMoDz/4DBlockEngine
 public bool Equals(Block other)
 {
     return m_type == other.m_type && m_color == other.m_color && m_lightSun == other.m_lightSun && m_lightRed == other.m_lightRed && m_lightGreen == other.m_lightGreen && m_lightBlue == other.m_lightBlue;
 }
コード例 #18
0
ファイル: PageManager.cs プロジェクト: HaKDMoDz/4DBlockEngine
	    public void LoadOrCreateChunk(Vector3Int position, Block[] blocks, MappingFunction mappingFunction)
        {
            var pagePositionX = position.X / Page.PageSizeInBlocks;
            var pagePositionY = position.Y / Page.PageSizeInBlocks;
            var pagePositionZ = position.Z / Page.PageSizeInBlocks;
            var pageId = CreatePageId(pagePositionX, pagePositionY, pagePositionZ);
            if (m_pageCache.ContainsPage(pageId))
            {
                var page = m_pageCache.GetPage(pageId);
                CopyPageToChunk(page, position, blocks, mappingFunction);
            }
            else if (m_directory.Pages.Contains(pageId))
            {
                var page = DecompressPage(pageId);
                CopyPageToChunk(page, position, blocks, mappingFunction);
                m_pageCache.InsertPage(page);
            }
            else if (!m_pagesPendingWrite.Contains(pageId))
            {
                m_pagesPendingWrite.Add(pageId);
                var page = new Page(position.X, position.Y, position.Z, pageId);
                var worldPosX = pagePositionX * Page.PageSizeInBlocks;
                var worldPosY = pagePositionY * Page.PageSizeInBlocks;
                var worldPosZ = pagePositionZ * Page.PageSizeInBlocks;
                TerrainGenerator.Instance.GenerateDataForChunk(worldPosX, worldPosY, worldPosZ,
                                                               Page.PageSizeInBlocks, page.Data, (x, y, z) => Page.BlockIndexFromRelativePosition(
                                                                   x - worldPosX, y - worldPosY, z - worldPosZ));

                //BlockDataUtilities.SetupScanDirectedHilbertCurve(Page.PageSizeInBlocks);
               // ChunkCompressor.GetHilbertCurve(32);
                var timer = Stopwatch.StartNew();
                float compressionRatio;
                //ChunkCompressor.ScanDirection scanDir;
                var tree = ChunkCompressor.GetCompressor(Page.PageSizeInBlocks).ConvertArrayToIntervalTree(page.Data,
                    out compressionRatio);
                Console.WriteLine("tree creation time: " + timer.ElapsedMilliseconds);
              //  Console.WriteLine("h: " + scanDir);
              //  Console.WriteLine("tree compresion ratio: " + compressionRatio);
              //  timer.Restart();
            //    var tree2 = ChunkCompressor.ConvertArrayToIntervalTreeLinear(page.Data, Page.PageSizeInBlocks,
            //       out compressionRatio, out scanDir);
                //Console.WriteLine("linear tree creation time: " + timer.ElapsedMilliseconds);
               // Console.WriteLine("l: " + scanDir);
              //  Console.WriteLine("l: " + compressionRatio);

               // tree.

                timer.Restart();
                var res = tree[new Interval(17490)];
                Console.WriteLine("tree query time: " + timer.ElapsedMilliseconds);
                using (var fileStream = new FileStream(Path.Combine(m_dataDirectory, page.PageId + "_tree.page"), FileMode.Create))
                {
                    using (var compressor = new GZipStream(fileStream, CompressionLevel.Optimal))
                    {
                        //Serializer.Serialize(compressor, tree);
                    }
                }
                Console.WriteLine("tree compressionTime time: " + timer.ElapsedMilliseconds);

                timer.Restart();
                CompressPage(page);
                Console.WriteLine("normal compressionTime time: " + timer.ElapsedMilliseconds);

                m_pageCache.InsertPage(page);
                m_directory.Pages.Add(pageId);
                CopyPageToChunk(page, position, blocks, mappingFunction);
                //m_jsonWriter.Write("SaveDirectory", m_directory);
              /*  Task.Run(() =>
                {
                    CompressPage(page);
                    m_pagesPendingWrite.Remove(page.PageId);
                });*/
            }
	    }
コード例 #19
0
ファイル: ChunkCache.cs プロジェクト: HaKDMoDz/4DBlockEngine
 public void AddBlock(int x, int y, int z, ref Block block)
 {
     var chunk = GetChunkByWorldPosition(x, y, z); // get the chunk that block is hosted in.
     if (chunk == null)
     {
         return;
     }
     var flattenIndex = BlockIndexByWorldPosition(x, y, z);
     Blocks[flattenIndex] = block;
     chunk.AddBlock(x, y, z);
     m_lightingEngine.AddBlock(x, y, z, block);
 }