public override void BuildFace(Chunk chunk, Vector3[] vertices, Color32[] palette, ref BlockFace face, bool rotated) { bool backFace = DirectionUtils.IsBackface(face.side); LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); Vector3[] verts = pools.vector3ArrayPool.PopExact(4); Color32[] cols = pools.color32ArrayPool.PopExact(4); { verts[0] = vertices[0]; verts[1] = vertices[1]; verts[2] = vertices[2]; verts[3] = vertices[3]; cols[0] = palette[face.block.type]; cols[1] = palette[face.block.type]; cols[2] = palette[face.block.type]; cols[3] = palette[face.block.type]; BlockUtils.AdjustColors(chunk, cols, face.light); RenderGeometryBatcher batcher = chunk.RenderGeometryHandler.Batcher; batcher.AddFace(face.materialID, verts, cols, backFace); } pools.color32ArrayPool.Push(cols); pools.vector3ArrayPool.Push(verts); }
public override void BuildFace(Chunk chunk, Vector3[] vertices, Color32[] palette, ref BlockFace face, bool rotated) { bool backface = DirectionUtils.IsBackface(face.side); int d = DirectionUtils.Get(face.side); LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); Vector3[] verts = pools.vector3ArrayPool.PopExact(4); Vector4[] uvs = pools.vector4ArrayPool.PopExact(4); Color32[] cols = pools.color32ArrayPool.PopExact(4); { if (vertices == null) { Vector3 pos = face.pos; verts[0] = pos + BlockUtils.paddingOffsets[d][0]; verts[1] = pos + BlockUtils.paddingOffsets[d][1]; verts[2] = pos + BlockUtils.paddingOffsets[d][2]; verts[3] = pos + BlockUtils.paddingOffsets[d][3]; } else { verts[0] = vertices[0]; verts[1] = vertices[1]; verts[2] = vertices[2]; verts[3] = vertices[3]; } cols[0] = Colors[d]; cols[1] = Colors[d]; cols[2] = Colors[d]; cols[3] = Colors[d]; BlockUtils.PrepareTexture(verts, uvs, face.side, Textures, rotated); BlockUtils.AdjustColors(chunk, cols, face.light); RenderGeometryBatcher batcher = chunk.RenderGeometryHandler.Batcher; batcher.AddFace(face.materialID, verts, cols, uvs, backface); } pools.color32ArrayPool.Push(cols); pools.vector4ArrayPool.Push(uvs); pools.vector3ArrayPool.Push(verts); }
public TaskPool() { Pools = new LocalPools(); items = new List <ITaskPoolItem>(); itemsP = new List <ITaskPoolItem>(); itemsTmp = new List <ITaskPoolItem>(); itemsTmpP = new List <ITaskPoolItem>(); @event = new AutoResetEvent(false); thread = new Thread(ThreadFunc) { IsBackground = true }; stop = false; hasPriorityItems = false; curr = max = currP = maxP = 0; }
public override void Build(Chunk chunk, out int minBounds, out int maxBounds) { ChunkBlocks blocks = chunk.Blocks; LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; int sizeWithPaddingPow3 = sizeWithPaddingPow2 * sizeWithPadding; bool[] mask = pools.boolArrayPool.PopExact(sizeWithPaddingPow3); Array.Clear(mask, 0, mask.Length); // This compression is essentialy RLE. However, instead of working on 1 axis // it works in 3 dimensions. int index = Env.CHUNK_PADDING + (Env.CHUNK_PADDING << pow) + (Env.CHUNK_PADDING << (pow << 1)); int yOffset = sizeWithPaddingPow2 - sideSize * sizeWithPadding; int zOffset = sizeWithPadding - sideSize; int minX = sideSize; int minY = sideSize; int minZ = sideSize; int maxX = 0; int maxY = 0; int maxZ = 0; for (int y = 0; y < sideSize; ++y, index += yOffset) { for (int z = 0; z < sideSize; ++z, index += zOffset) { for (int x = 0; x < sideSize; ++x, ++index) { // Skip already checked blocks if (mask[index]) { continue; } mask[index] = true; Block block = blocks.GetBlock(index); // Skip blocks we're not interested in right away if (!CanConsiderBlock(block)) { continue; } int x1 = x, y1 = y, z1 = z, x2 = x + 1, y2 = y + 1, z2 = z + 1; bool expandX = true; bool expandY = true; bool expandZ = true; bool expand; // Try to expand our box in all axes do { expand = false; if (expandY) { expandY = y2 < sideSize && ExpandY(blocks, ref mask, block, x1, z1, x2, ref y2, z2); expand = expandY; } if (expandZ) { expandZ = z2 < sideSize && ExpandZ(blocks, ref mask, block, x1, y1, x2, y2, ref z2); expand |= expandZ; } if (expandX) { expandX = x2 < sideSize && ExpandX(blocks, ref mask, block, y1, z1, ref x2, y2, z2); expand |= expandX; } } while (expand); BuildBox(chunk, block, x1, y1, z1, x2, y2, z2); // Calculate bounds if (x1 < minX) { minX = x1; } if (y1 < minY) { minY = y1; } if (z1 < minZ) { minZ = z1; } if (x2 > maxX) { maxX = x2; } if (y2 > maxY) { maxY = y2; } if (z2 > maxZ) { maxZ = z2; } } } } // Update chunk's geoemetry bounds minBounds = minX | (minY << 8) | (minZ << 16); maxBounds = maxX | (maxY << 8) | (maxZ << 16); pools.boolArrayPool.Push(mask); }
private void ProcessConfigs(World world, LayerCollection layers) { List <LayerConfigObject> layersConfigs = new List <LayerConfigObject>(layers.Layers); // Terrain layers List <TerrainLayer> terrainLayers = new List <TerrainLayer>(); List <int> terrainLayersIndexes = new List <int>(); // could be implemented as a HashSet, however, it would be insane storing hundreads of layers here // Structure layers List <TerrainLayer> structLayers = new List <TerrainLayer>(); List <int> structLayersIndexes = new List <int>(); // could be implemented as a HashSet, however, it would be insane storing hundreads of layers here for (int i = 0; i < layers.Layers.Length;) { LayerConfigObject config = layers.Layers[i]; // Set layers up TerrainLayer layer = config.GetLayer(); layer.BaseSetUp(config, world, this); if (layer.IsStructure) { // Do not allow any two layers share the same index if (structLayersIndexes.Contains(layer.Index)) { Debug.LogError("Could not create structure layer " + config.LayerName + ". Index " + layer.Index.ToString() + " already defined"); layersConfigs.RemoveAt(i); continue; } // Add layer to layers list structLayers.Add(layer); structLayersIndexes.Add(layer.Index); } else { // Do not allow any two layers share the same index if (terrainLayersIndexes.Contains(layer.Index)) { Debug.LogError("Could not create terrain layer " + config.LayerName + ". Index " + layer.Index.ToString() + " already defined"); layersConfigs.RemoveAt(i); continue; } // Add layer to layers list terrainLayers.Add(layer); terrainLayersIndexes.Add(layer.Index); } ++i; } // Call OnInit for each layer now that they all have been set up. Thanks to this, layers can // e.g. address other layers knowing that they will be able to access all data they need. int ti = 0, si = 0; for (int i = 0; i < layersConfigs.Count; i++) { LayerConfigObject config = layersConfigs[i]; if (config.IsStructure()) { structLayers[si++].Init(config); } else { terrainLayers[ti++].Init(config); } } // Sort the layers by index TerrainLayers = terrainLayers.ToArray(); Array.Sort(TerrainLayers); StructureLayers = structLayers.ToArray(); Array.Sort(StructureLayers); // Register support for noise functionality with each workpool thread for (int i = 0; i < Globals.WorkPool.Size; i++) { LocalPools pool = Globals.WorkPool.GetPool(i); pool.noiseItems = new NoiseItem[layersConfigs.Count]; for (int j = 0; j < layersConfigs.Count; j++) { pool.noiseItems[j] = new NoiseItem { noiseGen = new NoiseInterpolator() }; } } }
protected override void BuildBox(Chunk chunk, Block block, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { // Order of vertices when building faces: // 1--2 // | | // | | // 0--3 int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); ChunkBlocks blocks = chunk.Blocks; Chunk[] listeners = chunk.Neighbors; // Custom blocks have their own rules if (block.custom) { for (int yy = minY; yy < maxY; yy++) { for (int zz = minZ; zz < maxZ; zz++) { for (int xx = minX; xx < maxX; xx++) { Vector3Int pos = new Vector3Int(xx, yy, zz); block.BuildBlock(chunk, ref pos, block.renderMaterialID); } } } return; } int n, w, h, l, k, maskIndex; Vector3Int texturePos = new Vector3Int(minX, minY, minZ); Vector3[] face = pools.vector3ArrayPool.PopExact(4); BlockFace[] mask = pools.blockFaceArrayPool.PopExact(sideSize * sideSize); #region Top face if (listeners[(int)Direction.up] != null || // Don't render faces on world's edges for chunks with no neighbor (SideMask & Side.up) == 0 || maxY != sideSize) { Array.Clear(mask, 0, mask.Length); // x axis - width // z axis - height int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX, maxY, minZ, pow); int zOffset = sizeWithPadding - maxX + minX; // Build the mask for (int zz = minZ; zz < maxZ; ++zz, neighborIndex += zOffset) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++neighborIndex) { int currentIndex = neighborIndex - sizeWithPaddingPow2; // (xx, maxY-1, zz); Block neighborBlock = blocks.GetBlock(neighborIndex); // Let's see whether we can merge faces if (block.CanBuildFaceWith(neighborBlock)) { mask[n] = new BlockFace { block = block, pos = texturePos, side = Direction.up, light = BlockUtils.CalculateColors(chunk, currentIndex, Direction.up), materialID = block.renderMaterialID }; } } } // Build faces from the mask if it's possible for (int zz = minZ; zz < maxZ; ++zz) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n].block == null) { ++xx; ++n; continue; } // Compute width and height w = 1; h = 1; // Build the face bool rotated = mask[n].light.FaceRotationNecessary; if (!rotated) { face[0] = new Vector3(xx, maxY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.up][0]; face[1] = new Vector3(xx, maxY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.up][1]; face[2] = new Vector3(xx + w, maxY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.up][2]; face[3] = new Vector3(xx + w, maxY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.up][3]; } else { face[0] = new Vector3(xx, maxY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.up][1]; face[1] = new Vector3(xx + w, maxY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.up][2]; face[2] = new Vector3(xx + w, maxY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.up][3]; face[3] = new Vector3(xx, maxY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.up][0]; } block.BuildFace(chunk, face, Palette, ref mask[n], rotated); // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = new BlockFace(); } } xx += w; n += w; } } } #endregion #region Bottom face if (listeners[(int)Direction.down] != null || // Don't render faces on world's edges for chunks with no neighbor (SideMask & Side.down) == 0 || minY != 0) { Array.Clear(mask, 0, mask.Length); // x axis - width // z axis - height int currentIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY, minZ, pow); int zOffset = sizeWithPadding - maxX + minX; // Build the mask for (int zz = minZ; zz < maxZ; ++zz, currentIndex += zOffset) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++currentIndex) { int neighborIndex = currentIndex - sizeWithPaddingPow2; Block neighborBlock = blocks.GetBlock(neighborIndex); // Let's see whether we can merge faces if (block.CanBuildFaceWith(neighborBlock)) { mask[n] = new BlockFace { block = block, pos = texturePos, side = Direction.down, light = BlockUtils.CalculateColors(chunk, currentIndex, Direction.down), materialID = block.renderMaterialID }; } } } // Build faces from the mask if it's possible for (int zz = minZ; zz < maxZ; ++zz) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n].block == null) { ++xx; ++n; continue; } // Compute width and height w = 1; h = 1; // Build the face bool rotated = mask[n].light.FaceRotationNecessary; if (!rotated) { face[0] = new Vector3(xx, minY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.down][0]; face[1] = new Vector3(xx, minY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.down][1]; face[2] = new Vector3(xx + w, minY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.down][2]; face[3] = new Vector3(xx + w, minY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.down][3]; } else { face[0] = new Vector3(xx, minY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.down][1]; face[1] = new Vector3(xx + w, minY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.down][2]; face[2] = new Vector3(xx + w, minY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.down][3]; face[3] = new Vector3(xx, minY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.down][0]; } block.BuildFace(chunk, face, Palette, ref mask[n], rotated); // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = new BlockFace(); } } xx += w; n += w; } } } #endregion #region Right face if (listeners[(int)Direction.east] != null || // Don't render faces on world's edges for chunks with no neighbor (SideMask & Side.east) == 0 || maxX != sideSize) { Array.Clear(mask, 0, mask.Length); // y axis - height // z axis - width int neighborIndex = Helpers.GetChunkIndex1DFrom3D(maxX, minY, minZ, pow); int yOffset = sizeWithPaddingPow2 - (maxZ - minZ) * sizeWithPadding; // Build the mask for (int yy = minY; yy < maxY; ++yy, neighborIndex += yOffset) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ; ++zz, ++n, neighborIndex += sizeWithPadding) { int currentIndex = neighborIndex - 1; Block neighborBlock = blocks.GetBlock(neighborIndex); // Let's see whether we can merge faces if (block.CanBuildFaceWith(neighborBlock)) { mask[n] = new BlockFace { block = block, pos = texturePos, side = Direction.east, light = BlockUtils.CalculateColors(chunk, currentIndex, Direction.east), materialID = block.renderMaterialID }; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ;) { if (mask[n].block == null) { ++zz; ++n; continue; } // Compute width and height w = 1; h = 1; // Build the face bool rotated = mask[n].light.FaceRotationNecessary; if (!rotated) { face[0] = new Vector3(maxX, yy, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.east][0]; face[1] = new Vector3(maxX, yy + h, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.east][1]; face[2] = new Vector3(maxX, yy + h, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.east][2]; face[3] = new Vector3(maxX, yy, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.east][3]; } else { face[0] = new Vector3(maxX, yy + h, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.east][1]; face[1] = new Vector3(maxX, yy + h, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.east][2]; face[2] = new Vector3(maxX, yy, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.east][3]; face[3] = new Vector3(maxX, yy, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.east][0]; } block.BuildFace(chunk, face, Palette, ref mask[n], rotated); // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = new BlockFace(); } } zz += w; n += w; } } } #endregion #region Left face if (listeners[(int)Direction.west] != null || // Don't render faces on world's edges for chunks with no neighbor (SideMask & Side.west) == 0 || minX != 0) { Array.Clear(mask, 0, mask.Length); // y axis - height // z axis - width int currentIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY, minZ, pow); int yOffset = sizeWithPaddingPow2 - (maxZ - minZ) * sizeWithPadding; // Build the mask for (int yy = minY; yy < maxY; ++yy, currentIndex += yOffset) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ; ++zz, ++n, currentIndex += sizeWithPadding) { int neighborIndex = currentIndex - 1; Block neighborBlock = blocks.GetBlock(neighborIndex); // Let's see whether we can merge faces if (block.CanBuildFaceWith(neighborBlock)) { mask[n] = new BlockFace { block = block, pos = texturePos, side = Direction.west, light = BlockUtils.CalculateColors(chunk, currentIndex, Direction.west), materialID = block.renderMaterialID }; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ;) { if (mask[n].block == null) { ++zz; ++n; continue; } // Compute width and height w = 1; h = 1; // Build the face bool rotated = mask[n].light.FaceRotationNecessary; if (!rotated) { face[0] = new Vector3(minX, yy, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.west][0]; face[1] = new Vector3(minX, yy + h, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.west][1]; face[2] = new Vector3(minX, yy + h, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.west][2]; face[3] = new Vector3(minX, yy, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.west][3]; } else { face[0] = new Vector3(minX, yy + h, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.west][1]; face[1] = new Vector3(minX, yy + h, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.west][2]; face[2] = new Vector3(minX, yy, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.west][3]; face[3] = new Vector3(minX, yy, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.west][0]; } block.BuildFace(chunk, face, Palette, ref mask[n], rotated); // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = new BlockFace(); } } zz += w; n += w; } } } #endregion #region Front face if (listeners[(int)Direction.north] != null || // Don't render faces on world's edges for chunks with no neighbor (SideMask & Side.north) == 0 || maxZ != sideSize) { Array.Clear(mask, 0, mask.Length); // x axis - width // y axis - height int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY, maxZ, pow); int yOffset = sizeWithPaddingPow2 - maxX + minX; // Build the mask for (int yy = minY; yy < maxY; ++yy, neighborIndex += yOffset) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++neighborIndex) { int currentIndex = neighborIndex - sizeWithPadding; Block neighborBlock = blocks.GetBlock(neighborIndex); // Let's see whether we can merge faces if (block.CanBuildFaceWith(neighborBlock)) { mask[n] = new BlockFace { block = block, pos = texturePos, side = Direction.north, light = BlockUtils.CalculateColors(chunk, currentIndex, Direction.north), materialID = block.renderMaterialID }; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n].block == null) { ++xx; ++n; continue; } // Compute width and height w = 1; h = 1; // Build the face bool rotated = mask[n].light.FaceRotationNecessary; if (!rotated) { face[0] = new Vector3(xx, yy, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][0]; face[1] = new Vector3(xx, yy + h, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][1]; face[2] = new Vector3(xx + w, yy + h, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][2]; face[3] = new Vector3(xx + w, yy, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][3]; } else { face[0] = new Vector3(xx, yy + h, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][1]; face[1] = new Vector3(xx + w, yy + h, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][2]; face[2] = new Vector3(xx + w, yy, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][3]; face[3] = new Vector3(xx, yy, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][0]; } block.BuildFace(chunk, face, Palette, ref mask[n], rotated); // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = new BlockFace(); } } xx += w; n += w; } } } #endregion #region Back face if (listeners[(int)Direction.south] != null || // Don't render faces on world's edges for chunks with no neighbor (SideMask & Side.south) == 0 || minZ != 0) { Array.Clear(mask, 0, mask.Length); // x axis - width // y axis - height int currentIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY, minZ, pow); int yOffset = sizeWithPaddingPow2 - maxX + minX; // Build the mask for (int yy = minY; yy < maxY; ++yy, currentIndex += yOffset) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++currentIndex) { int neighborIndex = currentIndex - sizeWithPadding; Block neighborBlock = blocks.GetBlock(neighborIndex); // Let's see whether we can merge faces if (block.CanBuildFaceWith(neighborBlock)) { mask[n] = new BlockFace { block = block, pos = texturePos, side = Direction.south, light = BlockUtils.CalculateColors(chunk, currentIndex, Direction.south), materialID = block.renderMaterialID }; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n].block == null) { ++xx; ++n; continue; } // Compute width and height w = 1; h = 1; // Build the face bool rotated = mask[n].light.FaceRotationNecessary; if (!rotated) { face[0] = new Vector3(xx, yy, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][0]; face[1] = new Vector3(xx, yy + h, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][1]; face[2] = new Vector3(xx + w, yy + h, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][2]; face[3] = new Vector3(xx + w, yy, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][3]; } else { face[0] = new Vector3(xx, yy + h, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][1]; face[1] = new Vector3(xx + w, yy + h, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][2]; face[2] = new Vector3(xx + w, yy, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][3]; face[3] = new Vector3(xx, yy, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][0]; } block.BuildFace(chunk, face, Palette, ref mask[n], rotated); // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = new BlockFace(); } } xx += w; n += w; } } } #endregion pools.blockFaceArrayPool.Push(mask); pools.vector3ArrayPool.Push(face); }
/// <summary> /// Compresses chunk's memory. /// </summary> public void Compress() { int sizePlusPadding = sideSize + Env.CHUNK_PADDING; int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; int sizeWithPaddingPow3 = sizeWithPaddingPow2 * sizeWithPadding; LocalPools pools = Globals.WorkPool.GetPool(Chunk.ThreadID); bool[] mask = pools.boolArrayPool.PopExact(sizeWithPaddingPow3); Array.Clear(mask, 0, mask.Length); blockCompressed.Clear(); // This compression is essentialy RLE. However, instead of working on 1 axis // it works in 3 dimensions. int index = 0; for (int y = -1; y < sizePlusPadding; ++y) { for (int z = -1; z < sizePlusPadding; ++z) { for (int x = -1; x < sizePlusPadding; ++x, ++index) { // Skip already checked blocks if (mask[index]) { continue; } mask[index] = true; // Skip air data ushort data = this[index].Data; ushort type = (ushort)(data & BlockData.typeMask); if (type == BlockProvider.AIR_TYPE) { continue; } int x1 = x, y1 = y, z1 = z, x2 = x + 1, y2 = y + 1, z2 = z + 1; bool expandX = true; bool expandY = true; bool expandZ = true; bool expand; // Try to expand our box in all axes do { expand = false; if (expandY) { expandY = y2 < sizePlusPadding && ExpandY(ref mask, type, x1, z1, x2, ref y2, z2); expand = expandY; } if (expandZ) { expandZ = z2 < sizePlusPadding && ExpandZ(ref mask, type, x1, y1, x2, y2, ref z2); expand |= expandZ; } if (expandX) { expandX = x2 < sizePlusPadding && ExpandX(ref mask, type, y1, z1, ref x2, y2, z2); expand |= expandX; } } while (expand); blockCompressed.Add(new BlockDataAABB(data, x1, y1, z1, x2, y2, z2)); /*// Let's make sure that we don't take too much space * int compressedSize = blockCompressed.Count*StructSerialization.TSSize<BlockDataAABB>.ValueSize; * int decompressedSize = sizeWithPaddingPow3*StructSerialization.TSSize<BlockData>.ValueSize; * if (compressedSize>=(decompressedSize>>1)) * { * blockCompressed.Clear(); * pools.BoolArrayPool.Push(mask); * return; * }*/ } } } pools.boolArrayPool.Push(mask); }
public override void BuildBlock(Chunk chunk, ref Vector3Int localPos, int materialID) { LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); RenderGeometryBatcher batcher = chunk.RenderGeometryHandler.Batcher; // Using the block positions hash is much better for random numbers than saving the offset and height in the block data int hash = localPos.GetHashCode(); hash *= 39; float offsetX = (hash & 63) * COEF * Env.BLOCK_SIZE_HALF - Env.BLOCK_SIZE_HALF * 0.5f; hash *= 39; float offsetZ = (hash & 63) * COEF * Env.BLOCK_SIZE_HALF - Env.BLOCK_SIZE_HALF * 0.5f; // Converting the position to a vector adjusts it based on block size and gives us real world coordinates for x, y and z Vector3 vPos = localPos; vPos += new Vector3(offsetX, 0, offsetZ); float x1 = vPos.x - BlockUtils.blockPadding; float x2 = vPos.x + BlockUtils.blockPadding + Env.BLOCK_SIZE; float y1 = vPos.y - BlockUtils.blockPadding; float y2 = vPos.y + BlockUtils.blockPadding + Env.BLOCK_SIZE; float z1 = vPos.z - BlockUtils.blockPadding; float z2 = vPos.z + BlockUtils.blockPadding + Env.BLOCK_SIZE; Vector3[] verts = pools.vector3ArrayPool.PopExact(4); Vector4[] uvs = pools.vector4ArrayPool.PopExact(4); Color32[] colors = pools.color32ArrayPool.PopExact(4); { colors[0] = Color; colors[1] = Color; colors[2] = Color; colors[3] = Color; } { verts[0] = new Vector3(x1, y1, z2); verts[1] = new Vector3(x1, y2, z2); verts[2] = new Vector3(x2, y2, z1); verts[3] = new Vector3(x2, y1, z1); // Needs to have some vertices before being able to get a texture. BlockUtils.PrepareTexture(verts, uvs, Direction.north, Texture, false); batcher.AddFace(materialID, verts, colors, uvs, false); } { verts[0] = new Vector3(x2, y1, z1); verts[1] = new Vector3(x2, y2, z1); verts[2] = new Vector3(x1, y2, z2); verts[3] = new Vector3(x1, y1, z2); batcher.AddFace(materialID, verts, colors, uvs, false); } { verts[0] = new Vector3(x2, y1, z2); verts[1] = new Vector3(x2, y2, z2); verts[2] = new Vector3(x1, y2, z1); verts[3] = new Vector3(x1, y1, z1); batcher.AddFace(materialID, verts, colors, uvs, false); } { verts[0] = new Vector3(x1, y1, z1); verts[1] = new Vector3(x1, y2, z1); verts[2] = new Vector3(x2, y2, z2); verts[3] = new Vector3(x2, y1, z2); batcher.AddFace(materialID, verts, colors, uvs, false); } pools.color32ArrayPool.Push(colors); pools.vector4ArrayPool.Push(uvs); pools.vector3ArrayPool.Push(verts); }
protected override void BuildBox(Chunk chunk, Block block, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { // Order of vertices when building faces: // 1--2 // | | // | | // 0--3 int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); ChunkBlocks blocks = chunk.Blocks; Chunk[] listeners = chunk.Neighbors; // Custom blocks have their own rules // TODO: Implement custom block colliders /*if (block.Custom) * { * for (int yy = minY; yy < maxY; yy++) * { * for (int zz = minZ; zz < maxZ; zz++) * { * for (int xx = minX; xx < maxX; xx++) * { * ... // build collider here * } * } * } * * return; * }*/ Vector3[] vertexData = pools.vector3ArrayPool.PopExact(4); bool[] mask = pools.boolArrayPool.PopExact(sideSize * sideSize); int n, w, h, l, k, maskIndex; #region Top face if (listeners[(int)Direction.up] != null || // Don't render faces on world's edges for chunks with no neighbor maxY != sideSize) { Array.Clear(mask, 0, mask.Length); // x axis - width // z axis - height int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX, maxY, minZ, pow); int zOffset = sizeWithPadding - maxX + minX; // Build the mask for (int zz = minZ; zz < maxZ; ++zz, neighborIndex += zOffset) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++neighborIndex) { // Let's see whether we can merge the faces if (!blocks.GetBlock(neighborIndex).CanCollide) { mask[n] = true; } } } // Build faces from the mask if it's possible for (int zz = minZ; zz < maxZ; ++zz) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n] == false) { ++xx; ++n; continue; } bool m = mask[n]; // Compute width for (w = 1; xx + w < sideSize && mask[n + w] == m; w++) { } // Compute height for (h = 1; zz + h < sideSize; h++) { for (k = 0; k < w; k++) { maskIndex = n + k + h * sideSize; if (mask[maskIndex] == false || mask[maskIndex] != m) { goto cont; } } } cont: // Build the face { vertexData[0] = new Vector3(xx, maxY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.up][0]; vertexData[1] = new Vector3(xx, maxY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.up][1]; vertexData[2] = new Vector3(xx + w, maxY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.up][2]; vertexData[3] = new Vector3(xx + w, maxY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.up][3]; chunk.ColliderGeometryHandler.Batcher.AddFace(block.physicMaterialID, vertexData, DirectionUtils.IsBackface(Direction.up)); } // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = false; } } xx += w; n += w; } } } #endregion #region Bottom face if (listeners[(int)Direction.down] != null || // Don't render faces on world's edges for chunks with no neighbor minY != 0) { Array.Clear(mask, 0, mask.Length); // x axis - width // z axis - height int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY - 1, minZ, pow); int zOffset = sizeWithPadding - maxX + minX; // Build the mask for (int zz = minZ; zz < maxZ; ++zz, neighborIndex += zOffset) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++neighborIndex) { // Let's see whether we can merge the faces if (!blocks.GetBlock(neighborIndex).CanCollide) { mask[n] = true; } } } // Build faces from the mask if it's possible for (int zz = minZ; zz < maxZ; ++zz) { n = minX + zz * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n] == false) { ++xx; ++n; continue; } bool m = mask[n]; // Compute width for (w = 1; xx + w < sideSize && mask[n + w] == m; w++) { } // Compute height for (h = 1; zz + h < sideSize; h++) { for (k = 0; k < w; k++) { maskIndex = n + k + h * sideSize; if (mask[maskIndex] == false || mask[maskIndex] != m) { goto cont; } } } cont: // Build the face { vertexData[0] = new Vector3(xx, minY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.down][0]; vertexData[1] = new Vector3(xx, minY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.down][1]; vertexData[2] = new Vector3(xx + w, minY, zz + h) * scale + BlockUtils.paddingOffsets[(int)Direction.down][2]; vertexData[3] = new Vector3(xx + w, minY, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.down][3]; chunk.ColliderGeometryHandler.Batcher.AddFace(block.physicMaterialID, vertexData, DirectionUtils.IsBackface(Direction.down)); } // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = false; } } xx += w; n += w; } } } #endregion #region Right face if (listeners[(int)Direction.east] != null || // Don't render faces on world's edges for chunks with no neighbor maxX != sideSize) { Array.Clear(mask, 0, mask.Length); // y axis - height // z axis - width int neighborIndex = Helpers.GetChunkIndex1DFrom3D(maxX, minY, minZ, pow); int yOffset = sizeWithPaddingPow2 - (maxZ - minZ) * sizeWithPadding; // Build the mask for (int yy = minY; yy < maxY; ++yy, neighborIndex += yOffset) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ; ++zz, ++n, neighborIndex += sizeWithPadding) { // Let's see whether we can merge the faces if (!blocks.GetBlock(neighborIndex).CanCollide) { mask[n] = true; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ;) { if (mask[n] == false) { ++zz; ++n; continue; } bool m = mask[n]; // Compute width for (w = 1; zz + w < sideSize && mask[n + w] == m; w++) { } // Compute height for (h = 1; yy + h < sideSize; h++) { for (k = 0; k < w; k++) { maskIndex = n + k + h * sideSize; if (mask[maskIndex] == false || mask[maskIndex] != m) { goto cont; } } } cont: // Build the face { vertexData[0] = new Vector3(maxX, yy, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.east][0]; vertexData[1] = new Vector3(maxX, yy + h, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.east][1]; vertexData[2] = new Vector3(maxX, yy + h, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.east][2]; vertexData[3] = new Vector3(maxX, yy, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.east][3]; chunk.ColliderGeometryHandler.Batcher.AddFace(block.physicMaterialID, vertexData, DirectionUtils.IsBackface(Direction.east)); } // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = false; } } zz += w; n += w; } } } #endregion #region Left face if (listeners[(int)Direction.west] != null || // Don't render faces on world's edges for chunks with no neighbor minX != 0) { Array.Clear(mask, 0, mask.Length); // y axis - height // z axis - width int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX - 1, minY, minZ, pow); int yOffset = sizeWithPaddingPow2 - (maxZ - minZ) * sizeWithPadding; // Build the mask for (int yy = minY; yy < maxY; ++yy, neighborIndex += yOffset) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ; ++zz, ++n, neighborIndex += sizeWithPadding) { // Let's see whether we can merge the faces if (!blocks.GetBlock(neighborIndex).CanCollide) { mask[n] = true; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minZ + yy * sideSize; for (int zz = minZ; zz < maxZ;) { if (mask[n] == false) { ++zz; ++n; continue; } bool m = mask[n]; // Compute width for (w = 1; zz + w < sideSize && mask[n + w] == m; w++) { } // Compute height for (h = 1; yy + h < sideSize; h++) { for (k = 0; k < w; k++) { maskIndex = n + k + h * sideSize; if (mask[maskIndex] == false || mask[maskIndex] != m) { goto cont; } } } cont: // Build the face { vertexData[0] = new Vector3(minX, yy, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.west][0]; vertexData[1] = new Vector3(minX, yy + h, zz) * scale + BlockUtils.paddingOffsets[(int)Direction.west][1]; vertexData[2] = new Vector3(minX, yy + h, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.west][2]; vertexData[3] = new Vector3(minX, yy, zz + w) * scale + BlockUtils.paddingOffsets[(int)Direction.west][3]; chunk.ColliderGeometryHandler.Batcher.AddFace(block.physicMaterialID, vertexData, DirectionUtils.IsBackface(Direction.west)); } // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = false; } } zz += w; n += w; } } } #endregion #region Front face if (listeners[(int)Direction.north] != null || // Don't render faces on world's edges for chunks with no neighbor maxZ != sideSize) { Array.Clear(mask, 0, mask.Length); // x axis - width // y axis - height int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY, maxZ, pow); int yOffset = sizeWithPaddingPow2 - maxX + minX; // Build the mask for (int yy = minY; yy < maxY; ++yy, neighborIndex += yOffset) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++neighborIndex) { // Let's see whether we can merge the faces if (!blocks.GetBlock(neighborIndex).CanCollide) { mask[n] = true; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n] == false) { ++xx; ++n; continue; } bool m = mask[n]; // Compute width for (w = 1; xx + w < sideSize && mask[n + w] == m; w++) { } // Compute height for (h = 1; yy + h < sideSize; h++) { for (k = 0; k < w; k++) { maskIndex = n + k + h * sideSize; if (mask[maskIndex] == false || mask[maskIndex] != m) { goto cont; } } } cont: // Build the face { vertexData[0] = new Vector3(xx, yy, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][0]; vertexData[1] = new Vector3(xx, yy + h, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][1]; vertexData[2] = new Vector3(xx + w, yy + h, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][2]; vertexData[3] = new Vector3(xx + w, yy, maxZ) * scale + BlockUtils.paddingOffsets[(int)Direction.north][3]; chunk.ColliderGeometryHandler.Batcher.AddFace(block.physicMaterialID, vertexData, DirectionUtils.IsBackface(Direction.north)); } // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = false; } } xx += w; n += w; } } } #endregion #region Back face if (listeners[(int)Direction.south] != null || // Don't render faces on world's edges for chunks with no neighbor minZ != 0) { Array.Clear(mask, 0, mask.Length); // x axis - width // y axis - height int neighborIndex = Helpers.GetChunkIndex1DFrom3D(minX, minY, minZ - 1, pow); int yOffset = sizeWithPaddingPow2 - maxX + minX; // Build the mask for (int yy = minY; yy < maxY; ++yy, neighborIndex += yOffset) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX; ++xx, ++n, ++neighborIndex) { // Let's see whether we can merge the faces if (!blocks.GetBlock(neighborIndex).CanCollide) { mask[n] = true; } } } // Build faces from the mask if it's possible for (int yy = minY; yy < maxY; ++yy) { n = minX + yy * sideSize; for (int xx = minX; xx < maxX;) { if (mask[n] == false) { ++xx; ++n; continue; } bool m = mask[n]; // Compute width for (w = 1; xx + w < sideSize && mask[n + w] == m; w++) { } // Compute height for (h = 1; yy + h < sideSize; h++) { for (k = 0; k < w; k++) { maskIndex = n + k + h * sideSize; if (mask[maskIndex] == false || mask[maskIndex] != m) { goto cont; } } } cont: // Build the face { vertexData[0] = new Vector3(xx, yy, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][0]; vertexData[1] = new Vector3(xx, yy + h, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][1]; vertexData[2] = new Vector3(xx + w, yy + h, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][2]; vertexData[3] = new Vector3(xx + w, yy, minZ) * scale + BlockUtils.paddingOffsets[(int)Direction.south][3]; chunk.ColliderGeometryHandler.Batcher.AddFace(block.physicMaterialID, vertexData, DirectionUtils.IsBackface(Direction.south)); } // Zero out the mask. We don't need to process the same fields again for (l = 0; l < h; ++l) { maskIndex = n + l * sideSize; for (k = 0; k < w; ++k, ++maskIndex) { mask[maskIndex] = false; } } xx += w; n += w; } } } #endregion pools.boolArrayPool.Push(mask); pools.vector3ArrayPool.Push(vertexData); }
public bool DoDecompression() { LocalPools pools = Globals.WorkPool.GetPool(Chunk.ThreadID); BlockProvider provider = Chunk.World.blockProvider; if (IsDifferential) { int blockPosSize = StructSerialization.TSSize <BlockPos> .ValueSize; int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; positionsModified = new BlockPos[positionsBytes.Length / blockPosSize]; blocksModified = new BlockData[blocksBytes.Length / blockDataSize]; int i, j; unsafe { // Extract positions fixed(byte *pSrc = positionsBytes) { for (i = 0, j = 0; j < positionsModified.Length; i += blockPosSize, j++) { positionsModified[j] = *(BlockPos *)&pSrc[i]; Chunk.Blocks.modifiedBlocks.Add(positionsModified[j]); } } // Extract block data fixed(byte *pSrc = blocksBytes) { for (i = 0, j = 0; j < blocksModified.Length; i += blockDataSize, j++) { BlockData *bd = (BlockData *)&pSrc[i]; // Convert global block types into internal optimized version ushort type = provider.GetTypeFromTypeInConfig(bd->Type); blocksModified[j] = new BlockData(type, bd->Solid); } } } } else { int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; int requestedByteSize = Env.CHUNK_SIZE_POW_3 * blockDataSize; // Pop a large enough buffers from the pool byte[] bytes = pools.byteArrayPool.Pop(requestedByteSize); { // Decompress data int decompressedLength = CLZF2.lzf_decompress(blocksBytes, blocksBytes.Length, ref bytes); if (decompressedLength != Env.CHUNK_SIZE_POW_3 * blockDataSize) { blocksBytes = null; return(false); } // Fill chunk with decompressed data ChunkBlocks blocks = Chunk.Blocks; int i = 0; unsafe { fixed(byte *pSrc = bytes) { int index = Helpers.ZERO_CHUNK_INDEX; int yOffset = Env.CHUNK_SIZE_WITH_PADDING_POW_2 - Env.CHUNK_SIZE * Env.CHUNK_SIZE_WITH_PADDING; int zOffset = Env.CHUNK_SIZE_WITH_PADDING - Env.CHUNK_SIZE; for (int y = 0; y < Env.CHUNK_SIZE; ++y, index += yOffset) { for (int z = 0; z < Env.CHUNK_SIZE; ++z, index += zOffset) { for (int x = 0; x < Env.CHUNK_SIZE; ++x, i += blockDataSize, ++index) { BlockData *bd = (BlockData *)&pSrc[i]; // Convert global block type into internal optimized version ushort type = provider.GetTypeFromTypeInConfig(bd->Type); blocks.SetRaw(index, new BlockData(type, bd->Solid)); } } } } } } // Return our temporary buffer back to the pool pools.byteArrayPool.Push(bytes); } ResetTemporary(); return(true); }
public bool DoCompression() { if (Features.useDifferentialSerialization) { BlockProvider provider = Chunk.World.blockProvider; int blockPosSize = StructSerialization.TSSize <BlockPos> .ValueSize; int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; int posLenBytes = blocksModified.Length * blockPosSize; int blkLenBytes = blocksModified.Length * blockDataSize; positionsBytes = new byte[posLenBytes]; blocksBytes = new byte[blkLenBytes]; unsafe { // Pack positions to a byte array fixed(byte *pDst = positionsBytes) { for (int i = 0, j = 0; i < blocksModified.Length; i++, j += blockPosSize) { *(BlockPos *)&pDst[j] = positionsModified[i]; } } // Pack block data to a byte array fixed(BlockData *pBD = blocksModified) fixed(byte *pDst = blocksBytes) { for (int i = 0, j = 0; i < blocksModified.Length; i++, j += blockDataSize) { BlockData *bd = &pBD[i]; // Convert block types from internal optimized version into global types ushort typeInConfig = provider.GetConfig(bd->Type).TypeInConfig; *(BlockData *)&pDst[j] = new BlockData(typeInConfig, bd->Solid); } } } } else { LocalPools pools = Globals.WorkPool.GetPool(Chunk.ThreadID); BlockProvider provider = Chunk.World.blockProvider; int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; int requestedByteSize = Env.CHUNK_SIZE_POW_3 * blockDataSize; // Pop large enough buffers from the pool byte[] tmp = pools.byteArrayPool.Pop(requestedByteSize); byte[] bytesCompressed = pools.byteArrayPool.Pop(requestedByteSize); { ChunkBlocks blocks = Chunk.Blocks; int i = 0; int index = Helpers.ZERO_CHUNK_INDEX; int yOffset = Env.CHUNK_SIZE_WITH_PADDING_POW_2 - Env.CHUNK_SIZE * Env.CHUNK_SIZE_WITH_PADDING; int zOffset = Env.CHUNK_SIZE_WITH_PADDING - Env.CHUNK_SIZE; for (int y = 0; y < Env.CHUNK_SIZE; ++y, index += yOffset) { for (int z = 0; z < Env.CHUNK_SIZE; ++z, index += zOffset) { for (int x = 0; x < Env.CHUNK_SIZE; ++x, i += blockDataSize, ++index) { BlockData bd = blocks.Get(index); // Convert block types from internal optimized version into global types ushort typeInConfig = provider.GetConfig(bd.Type).TypeInConfig; // Write updated block data to destination buffer unsafe { fixed(byte *pDst = tmp) { *(BlockData *)&pDst[i] = new BlockData(typeInConfig, bd.Solid); } } } } } // Compress bytes int blkLenBytes = CLZF2.lzf_compress(tmp, requestedByteSize, ref bytesCompressed); blocksBytes = new byte[blkLenBytes]; // Copy data from a temporary buffer to block buffer Array.Copy(bytesCompressed, 0, blocksBytes, 0, blkLenBytes); } // Return our temporary buffer back to the pool pools.byteArrayPool.Push(bytesCompressed); pools.byteArrayPool.Push(tmp); } return(true); }