/// <summary> /// Decompresses chunk's memory. /// </summary> public void Decompress() { int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; for (int i = 0; i < blockCompressed.Count; i++) { BlockDataAABB box = blockCompressed[i]; int x1 = box.MinX; int y1 = box.MinY; int z1 = box.MinZ; int x2 = box.MaxX; int y2 = box.MaxY; int z2 = box.MaxZ; ushort data = box.Data; int index = Helpers.GetChunkIndex1DFrom3D(x1, y1, z1, pow); int yOffset = sizeWithPaddingPow2 - (z2 - z1) * sizeWithPadding; int zOffset = sizeWithPadding - (x2 - x1); for (int y = y1; y < y2; ++y, index += yOffset) { for (int z = z1; z < z2; ++z, index += zOffset) { for (int x = x1; x < x2; ++x, ++index) { this[index] = new BlockData(data); } } } } }
private bool ExpandX(ref bool[] mask, ushort type, int y1, int z1, ref int x2, int y2, int z2) { int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; int yOffset = sizeWithPaddingPow2 - (z2 - z1) * sizeWithPadding; int index0 = Helpers.GetChunkIndex1DFrom3D(x2, y1, z1, pow); // Check the quad formed by YZ axes and try to expand the X asix int index = index0; for (int y = y1; y < y2; ++y, index += yOffset) { for (int z = z1; z < z2; ++z, index += sizeWithPadding) { if (mask[index] || this[index].Type != type) { return(false); } } } // If the box can expand, mark the position as tested and expand the X axis index = index0; for (int y = y1; y < y2; ++y, index += yOffset) { for (int z = z1; z < z2; ++z, index += sizeWithPadding) { mask[index] = true; } } ++x2; return(true); }
private bool ExpandZ(ChunkBlocks blocks, ref bool[] mask, Block block, int x1, int y1, int x2, int y2, ref int z2) { int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; int yOffset = sizeWithPaddingPow2 - x2 + x1; int index0 = Helpers.GetChunkIndex1DFrom3D(x1, y1, z2, pow); // Check the quad formed by XY axes and try to expand the Z axis int index = index0; for (int y = y1; y < y2; ++y, index += yOffset) { for (int x = x1; x < x2; ++x, ++index) { if (mask[index] || !CanCreateBox(block, blocks.GetBlock(index))) { return(false); } } } // If the box can expand, mark the position as tested and expand the X axis index = index0; for (int y = y1; y < y2; ++y, index += yOffset) { for (int x = x1; x < x2; ++x, ++index) { mask[index] = true; } } ++z2; return(true); }
private bool ExpandY(ref bool[] mask, ushort type, int x1, int z1, int x2, ref int y2, int z2) { int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int zOffset = sizeWithPadding - x2 + x1; int index0 = Helpers.GetChunkIndex1DFrom3D(x1, y2, z1, pow); // Check the quad formed by XZ axes and try to expand the Y axis int index = index0; for (int z = z1; z < z2; ++z, index += zOffset) { for (int x = x1; x < x2; ++x, ++index) { if (mask[index] || this[index].Type != type) { return(false); } } } // If the box can expand, mark the position as tested and expand the X axis index = index0; for (int z = z1; z < z2; ++z, index += zOffset) { for (int x = x1; x < x2; ++x, ++index) { mask[index] = true; } } ++y2; return(true); }
/// <summary> /// Queues a modification of blocks in a given range /// </summary> /// <param name="posFrom">Starting positon in local chunk coordinates</param> /// <param name="posTo">Ending position in local chunk coordinates</param> /// <param name="blockData">BlockData to place at the given location</param> /// <param name="setBlockModified">Set to true to mark chunk data as modified</param> /// <param name="onModified">Action to perform once the operation finished</param> public void ModifyBlockDataRanged(ref Vector3Int posFrom, ref Vector3Int posTo, BlockData blockData, bool setBlockModified, Action <ModifyBlockContext> onModified = null) { // Make sure the values are okay. FixValues(ref posFrom.x, ref posTo.x); FixValues(ref posFrom.y, ref posTo.y); FixValues(ref posFrom.z, ref posTo.z); Vector3Int chunkPosFrom = Helpers.ContainingChunkPos(ref posFrom); Vector3Int chunkPosTo = Helpers.ContainingChunkPos(ref posTo); ModifyBlockContext context = null; if (onModified != null) { context = new ModifyBlockContext(onModified, this, Helpers.GetChunkIndex1DFrom3D(posFrom.x, posFrom.y, posFrom.z), Helpers.GetChunkIndex1DFrom3D(posTo.x, posTo.y, posTo.z), blockData, setBlockModified); } // Update all chunks in range int minY = Helpers.Mod(posFrom.y, Env.CHUNK_SIZE); for (int cy = chunkPosFrom.y; cy <= chunkPosTo.y; cy += Env.CHUNK_SIZE, minY = 0) { int maxY = Math.Min(posTo.y - cy, Env.CHUNK_SIZE_1); int minZ = Helpers.Mod(posFrom.z, Env.CHUNK_SIZE); for (int cz = chunkPosFrom.z; cz <= chunkPosTo.z; cz += Env.CHUNK_SIZE, minZ = 0) { int maxZ = Math.Min(posTo.z - cz, Env.CHUNK_SIZE_1); int minX = Helpers.Mod(posFrom.x, Env.CHUNK_SIZE); for (int cx = chunkPosFrom.x; cx <= chunkPosTo.x; cx += Env.CHUNK_SIZE, minX = 0) { Vector3Int chunkPos = new Vector3Int(cx, cy, cz); Chunk chunk = GetChunk(ref chunkPos); if (chunk == null) { continue; } int maxX = Math.Min(posTo.x - cx, Env.CHUNK_SIZE_1); chunk.Modify( new ModifyOpCuboid( blockData, new Vector3Int(minX, minY, minZ), new Vector3Int(maxX, maxY, maxZ), setBlockModified, context) ); } } } }
public bool ConsumeChanges() { ChunkBlocks blocks = Chunk.Blocks; if (!Features.useDifferentialSerialization) { return(true); } if (Features.useDifferentialSerialization_ForceSaveHeaders) { if (blocks.modifiedBlocks.Count <= 0) { return(true); } } else { if (blocks.modifiedBlocks.Count <= 0) { return(false); } } Dictionary <BlockPos, BlockData> blocksDictionary = new Dictionary <BlockPos, BlockData>(); // Create a map of modified blocks and their positions // TODO: Depending on the amount of changes this could become a performance bottleneck for (int i = 0; i < blocks.modifiedBlocks.Count; i++) { BlockPos pos = blocks.modifiedBlocks[i]; // Remove any existing blocks in the dictionary. They come from the existing save and are overwritten blocksDictionary.Remove(pos); blocksDictionary.Add(pos, blocks.Get(Helpers.GetChunkIndex1DFrom3D(pos.x, pos.y, pos.z))); } int cnt = blocksDictionary.Keys.Count; if (cnt > 0) { blocksModified = new BlockData[cnt]; positionsModified = new BlockPos[cnt]; int index = 0; foreach (KeyValuePair <BlockPos, BlockData> pair in blocksDictionary) { blocksModified[index] = pair.Value; positionsModified[index] = pair.Key; ++index; } } return(true); }
/// <summary> /// Fills chunk with layer data starting at startPlaceHeight and ending at endPlaceHeight /// </summary> /// <param name="chunk">Chunk filled with data</param> /// <param name="x">Position on x axis in local coordinates</param> /// <param name="z">Position on z axis in local coordinates</param> /// <param name="startPlaceHeight">Starting position on y axis in world coordinates</param> /// <param name="endPlaceHeight">Ending position on y axis in world coordinates</param> /// <param name="blockData">Block data to set</param> protected static void SetBlocks(Chunk chunk, int x, int z, int startPlaceHeight, int endPlaceHeight, BlockData blockData) { int chunkY = chunk.Pos.y; int chunkYMax = chunkY + Env.CHUNK_SIZE; int y = startPlaceHeight > chunkY ? startPlaceHeight : chunkY; int yMax = endPlaceHeight < chunkYMax ? endPlaceHeight : chunkYMax; ChunkBlocks blocks = chunk.Blocks; int index = Helpers.GetChunkIndex1DFrom3D(x, y - chunkY, z); while (y++ < yMax) { blocks.SetRaw(index, blockData); index += Env.CHUNK_SIZE_WITH_PADDING_POW_2; } }
/// <summary> /// Sets the block data at given world coordinates /// </summary> /// <param name="pos">Global position of the block</param> /// <param name="blockData">A block to be placed on a given position</param> public void SetBlockData(ref Vector3Int pos, BlockData blockData) { // Transform the position into chunk coordinates Vector3Int chunkPos = Helpers.ContainingChunkPos(ref pos); Chunk chunk = GetChunk(ref chunkPos); if (chunk == null) { return; } int xx = Helpers.Mod(pos.x, Env.CHUNK_SIZE); int yy = Helpers.Mod(pos.y, Env.CHUNK_SIZE); int zz = Helpers.Mod(pos.z, Env.CHUNK_SIZE); chunk.Blocks.SetInner(Helpers.GetChunkIndex1DFrom3D(xx, yy, zz), blockData); }
public BlockData GetBlockData(Vector3Int pos) { // Transform the position into chunk coordinates Vector3Int chunkPos = Helpers.ContainingChunkPos(ref pos); Chunk chunk = GetChunk(ref chunkPos); if (chunk == null) { // Return air if the chunk that do not exist return(BlockProvider.airBlock); } int xx = Helpers.Mod(pos.x, Env.CHUNK_SIZE); int yy = Helpers.Mod(pos.y, Env.CHUNK_SIZE); int zz = Helpers.Mod(pos.z, Env.CHUNK_SIZE); return(chunk.Blocks.Get(Helpers.GetChunkIndex1DFrom3D(xx, yy, zz))); }
public void CommitChanges() { if (!IsDifferential) { return; } // Rewrite generated blocks with differential positions if (blocksModified != null) { for (int i = 0; i < blocksModified.Length; i++) { BlockPos pos = positionsModified[i]; Chunk.Blocks.SetRaw(Helpers.GetChunkIndex1DFrom3D(pos.x, pos.y, pos.z), blocksModified[i]); } } MarkAsProcessed(); }
/// <summary> /// Sets blocks to a given value in a given range. It does not perform any logic. It simply sets the blocks. /// Use this function only when generating the terrain and structures. /// </summary> /// <param name="posFrom">Starting position in local chunk coordinates</param> /// <param name="posTo">Ending position in local chunk coordinates</param> /// <param name="blockData">A block to be placed on a given position</param> public void SetRangeRaw(ref Vector3Int posFrom, ref Vector3Int posTo, BlockData blockData) { int sizeWithPadding = sideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; int index = Helpers.GetChunkIndex1DFrom3D(posFrom.x, posFrom.y, posFrom.z, pow); int yOffset = sizeWithPaddingPow2 - (posTo.z - posFrom.z + 1) * sizeWithPadding; int zOffset = sizeWithPadding - (posTo.x - posFrom.x + 1); for (int y = posFrom.y; y <= posTo.y; ++y, index += yOffset) { for (int z = posFrom.z; z <= posTo.z; ++z, index += zOffset) { for (int x = posFrom.x; x <= posTo.x; ++x, ++index) { SetRaw(index, blockData); } } } }
/// <summary> /// Sets the block data at given world coordinates, updates the chunk and its /// neighbors if the Update chunk flag is true or not set. /// </summary> /// <param name="pos">Global position of the block</param> /// <param name="blockData">The block be placed</param> /// <param name="setBlockModified">Set to true to mark chunk data as modified</param> /// <param name="onModified">Action to perform once the operation finished</param> public void ModifyBlockData(ref Vector3Int pos, BlockData blockData, bool setBlockModified, Action <ModifyBlockContext> onModified = null) { // Transform the position into chunk coordinates Vector3Int chunkPos = Helpers.ContainingChunkPos(ref pos); Chunk chunk = GetChunk(ref chunkPos); if (chunk == null) { return; } int index = Helpers.GetChunkIndex1DFrom3D( Helpers.Mod(pos.x, Env.CHUNK_SIZE), Helpers.Mod(pos.y, Env.CHUNK_SIZE), Helpers.Mod(pos.z, Env.CHUNK_SIZE) ); // Nothing for us to do if the block did not change BlockData oldBlockData = chunk.Blocks.Get(index); if (oldBlockData.Type == blockData.Type) { return; } ModifyBlockContext context = null; if (onModified != null) { context = new ModifyBlockContext(onModified, this, index, index, blockData, setBlockModified); } chunk.Modify(new ModifyOpBlock(blockData, index, setBlockModified, context)); }
public Rect GetTexture(Chunk chunk, ref Vector3Int localPos, Direction direction) { if (uvs.Count == 1) { return(uvs[0]); } if (textureType == TextureConfigType.Connected) { ChunkBlocks blocks = chunk.Blocks; int localPosIndex = Helpers.GetChunkIndex1DFrom3D(localPos.x, localPos.y, localPos.z); ushort blockType = blocks.Get(localPosIndex).Type; // Side blocks bool n_, _e, s_, _w; // Corner blocks bool nw, ne, se, sw; int index1, index2, index3; int sizeWithPadding = chunk.SideSize + Env.CHUNK_PADDING_2; int sizeWithPaddingPow2 = sizeWithPadding * sizeWithPadding; switch (direction) { case Direction.up: index1 = localPosIndex + sizeWithPaddingPow2; // + (0,1,0) index2 = index1 - sizeWithPadding; // - (0,0,1) index3 = index1 + sizeWithPadding; // + (0,0,1) sw = blocks.Get(index2 - 1).Type == blockType; // -1,1,-1 s_ = blocks.Get(index2).Type == blockType; // 0,1,-1 se = blocks.Get(index2 + 1).Type == blockType; // 1,1,-1 _w = blocks.Get(index1 - 1).Type == blockType; // -1,1, 0 _e = blocks.Get(index1 + 1).Type == blockType; // 1,1, 0 nw = blocks.Get(index3 - 1).Type == blockType; // -1,1, 1 n_ = blocks.Get(index3).Type == blockType; // 0,1, 1 ne = blocks.Get(index3 + 1).Type == blockType; // 1,1, 1 break; case Direction.down: index1 = localPosIndex - sizeWithPaddingPow2; // - (0,1,0) index2 = index1 - sizeWithPadding; // - (0,0,1) index3 = index1 + sizeWithPadding; // + (0,0,1) sw = blocks.Get(index2 - 1).Type == blockType; // -1,-1,-1 s_ = blocks.Get(index2).Type == blockType; // 0,-1,-1 se = blocks.Get(index2 + 1).Type == blockType; // 1,-1,-1 _w = blocks.Get(index1 - 1).Type == blockType; // -1,-1, 0 _e = blocks.Get(index1 + 1).Type == blockType; // 1,-1, 0 nw = blocks.Get(index3 - 1).Type == blockType; // -1,-1, 1 n_ = blocks.Get(index3).Type == blockType; // 0,-1, 1 ne = blocks.Get(index3 + 1).Type == blockType; // 1,-1, 1 break; case Direction.north: index1 = localPosIndex + sizeWithPadding; // + (0,0,1) index2 = index1 - sizeWithPaddingPow2; // - (0,1,0) index3 = index1 + sizeWithPaddingPow2; // + (0,1,0) sw = blocks.Get(index2 - 1).Type == blockType; // -1,-1,1 se = blocks.Get(index2 + 1).Type == blockType; // 1,-1,1 _w = blocks.Get(index1 - 1).Type == blockType; // -1, 0,1 _e = blocks.Get(index1 + 1).Type == blockType; // 1, 0,1 nw = blocks.Get(index3 - 1).Type == blockType; // -1, 1,1 s_ = blocks.Get(index2).Type == blockType; // 0,-1,1 n_ = blocks.Get(index3).Type == blockType; // 0, 1,1 ne = blocks.Get(index3 + 1).Type == blockType; // 1, 1,1 break; case Direction.south: index1 = localPosIndex - sizeWithPadding; // - (0,0,1) index2 = index1 - sizeWithPaddingPow2; // - (0,1,0) index3 = index1 + sizeWithPaddingPow2; // + (0,1,0) sw = blocks.Get(index2 - 1).Type == blockType; // -1,-1,-1 se = blocks.Get(index2 + 1).Type == blockType; // 1,-1,-1 _w = blocks.Get(index1 - 1).Type == blockType; // -1, 0,-1 _e = blocks.Get(index1 + 1).Type == blockType; // 1, 0,-1 nw = blocks.Get(index3 - 1).Type == blockType; // -1, 1,-1 s_ = blocks.Get(index2).Type == blockType; // 0,-1,-1 n_ = blocks.Get(index3).Type == blockType; // 0, 1,-1 ne = blocks.Get(index3 + 1).Type == blockType; // 1, 1,-1 break; case Direction.east: index1 = localPosIndex + 1; // + (1,0,0) index2 = index1 - sizeWithPaddingPow2; // - (0,1,0) index3 = index1 + sizeWithPaddingPow2; // + (0,1,0) sw = blocks.Get(index2 - sizeWithPadding).Type == blockType; // 1,-1,-1 s_ = blocks.Get(index2).Type == blockType; // 1,-1, 0 se = blocks.Get(index2 + sizeWithPadding).Type == blockType; // 1,-1, 1 _w = blocks.Get(index1 - sizeWithPadding).Type == blockType; // 1, 0,-1 _e = blocks.Get(index1 + sizeWithPadding).Type == blockType; // 1, 0, 1 nw = blocks.Get(index3 - sizeWithPadding).Type == blockType; // 1, 1,-1 n_ = blocks.Get(index3).Type == blockType; // 1, 1, 0 ne = blocks.Get(index3 + sizeWithPadding).Type == blockType; // 1, 1, 1 break; default: //case Direction.west: index1 = localPosIndex - 1; // - (1,0,0) index2 = index1 - sizeWithPaddingPow2; // - (0,1,0) index3 = index1 + sizeWithPaddingPow2; // + (0,1,0) sw = blocks.Get(index2 - sizeWithPadding).Type == blockType; // -1,-1,-1 s_ = blocks.Get(index2).Type == blockType; // -1,-1, 0 se = blocks.Get(index2 + sizeWithPadding).Type == blockType; // -1,-1, 1 _w = blocks.Get(index1 - sizeWithPadding).Type == blockType; // -1, 0,-1 _e = blocks.Get(index1 + sizeWithPadding).Type == blockType; // -1, 0, 1 nw = blocks.Get(index3 - sizeWithPadding).Type == blockType; // -1, 1,-1 n_ = blocks.Get(index3).Type == blockType; // -1, 1, 0 ne = blocks.Get(index3 + sizeWithPadding).Type == blockType; // -1, 1, 1 break; } int uvIndex = ConnectedTextures.GetTexture(n_, _e, s_, _w, nw, ne, se, sw); return(uvs[uvIndex]); } if (uvs.Count > 1) { int hash = localPos.GetHashCode(); if (hash < 0) { hash *= -1; } float randomNumber = (hash % 100) / 100f; randomNumber *= uvs.Count; return(uvs[(int)randomNumber]); } Debug.LogError("There were no textures for " + textureName); return(new Rect()); }
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); }
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 override void Build(Chunk chunk, int id, ref Vector3Int worldPos, TerrainLayer layer) { World world = chunk.World; int noise = Helpers.FastFloor(NoiseUtils.GetNoise(layer.Noise.Noise, worldPos.x, worldPos.y, worldPos.z, 1f, 3, 1f)); int leavesRange = MIN_CROWN_SIZE + noise; int leavesRange1 = leavesRange - 1; int trunkHeight = MIN_TRUNK_SIZE + noise; // Make the crown an ellipsoid flattened on the y axis float a2inv = 1.0f / (leavesRange * leavesRange); float b2inv = 1.0f / (leavesRange1 * leavesRange1); int x1 = worldPos.x - leavesRange; int x2 = worldPos.x + leavesRange; int y1 = worldPos.y + 1 + trunkHeight; int y2 = y1 + 1 + 2 * leavesRange1; int z1 = worldPos.z - leavesRange; int z2 = worldPos.z + leavesRange; int cx, cy, cz; int minX, minY, minZ; int maxX, maxY, maxZ; AABBInt bounds = new AABBInt(x1, worldPos.y, z1, x2, y2, z2); Vector3Int chunkPos = chunk.Pos; StructureInfo info = null; // Generate the crown Vector3Int posFrom = new Vector3Int(x1, y1, z1); Vector3Int posTo = new Vector3Int(x2, y2, z2); Vector3Int chunkPosFrom = Helpers.ContainingChunkPos(ref posFrom); Vector3Int chunkPosTo = Helpers.ContainingChunkPos(ref posTo); minY = Helpers.Mod(posFrom.y, Env.CHUNK_SIZE); for (cy = chunkPosFrom.y; cy <= chunkPosTo.y; cy += Env.CHUNK_SIZE, minY = 0) { maxY = System.Math.Min(posTo.y - cy, Env.CHUNK_SIZE_1); minZ = Helpers.Mod(posFrom.z, Env.CHUNK_SIZE); for (cz = chunkPosFrom.z; cz <= chunkPosTo.z; cz += Env.CHUNK_SIZE, minZ = 0) { maxZ = System.Math.Min(posTo.z - cz, Env.CHUNK_SIZE_1); minX = Helpers.Mod(posFrom.x, Env.CHUNK_SIZE); for (cx = chunkPosFrom.x; cx <= chunkPosTo.x; cx += Env.CHUNK_SIZE, minX = 0) { maxX = System.Math.Min(posTo.x - cx, Env.CHUNK_SIZE_1); int xOff = cx - worldPos.x; int yOff = cy - y1 - leavesRange1; int zOff = cz - worldPos.z; if (cx != chunk.Pos.x || cy != chunk.Pos.y || cz != chunk.Pos.z) { Vector3Int pos = new Vector3Int(cx, cy, cz); Vector3Int min = new Vector3Int(minX, minY, minZ); Vector3Int max = new Vector3Int(maxX, maxY, maxZ); if (info == null) { info = new StructureInfo(id, ref chunkPos, ref bounds); } world.RegisterPendingStructure(info, new StructureContextTreeCrown(id, ref pos, ref worldPos, ref min, ref max, noise)); continue; } // Actual crown construction ChunkBlocks blocks = chunk.Blocks; int index = Helpers.GetChunkIndex1DFrom3D(minX, minY, minZ); int yOffset = Env.CHUNK_SIZE_WITH_PADDING_POW_2 - (maxZ - minZ + 1) * Env.CHUNK_SIZE_WITH_PADDING; int zOffset = Env.CHUNK_SIZE_WITH_PADDING - (maxX - minX + 1); for (int y = minY; y <= maxY; ++y, index += yOffset) { for (int z = minZ; z <= maxZ; ++z, index += zOffset) { for (int x = minX; x <= maxX; ++x, ++index) { int xx = x + xOff; int yy = y + yOff; int zz = z + zOff; float _x = xx * xx * a2inv; float _y = yy * yy * b2inv; float _z = zz * zz * a2inv; if (_x + _y + _z <= 1.0f) { blocks.SetRaw(index, leaves); } } } } } } } // Generate the trunk posFrom = new Vector3Int(worldPos.x, worldPos.y, worldPos.z); posTo = new Vector3Int(worldPos.x, worldPos.y + trunkHeight, worldPos.z); chunkPosFrom = Helpers.ContainingChunkPos(ref posFrom); chunkPosTo = Helpers.ContainingChunkPos(ref posTo); cx = Helpers.MakeChunkCoordinate(worldPos.x); cz = Helpers.MakeChunkCoordinate(worldPos.z); int tx = Helpers.Mod(worldPos.x, Env.CHUNK_SIZE); int tz = Helpers.Mod(worldPos.z, Env.CHUNK_SIZE); minY = Helpers.Mod(posFrom.y, Env.CHUNK_SIZE); for (cy = chunkPosFrom.y; cy <= chunkPosTo.y; cy += Env.CHUNK_SIZE, minY = 0) { maxY = System.Math.Min(posTo.y - cy, Env.CHUNK_SIZE_1); if (cx != chunk.Pos.x || cy != chunk.Pos.y || cz != chunk.Pos.z) { Vector3Int pos = new Vector3Int(cx, cy, cz); Vector3Int min = new Vector3Int(tx, minY, tz); Vector3Int max = new Vector3Int(tx, maxY, tz); if (info == null) { info = new StructureInfo(id, ref chunkPos, ref bounds); } world.RegisterPendingStructure(info, new StructureContextTreeTrunk(id, ref pos, ref min, ref max)); continue; } // Actual trunk construction ChunkBlocks blocks = chunk.Blocks; int index = Helpers.GetChunkIndex1DFrom3D(tx, minY, tz); for (int y = minY; y <= maxY; ++y, index += Env.CHUNK_SIZE_WITH_PADDING_POW_2) { blocks.SetRaw(index, log); } } }
public BlockData Get(ref Vector3Int pos) { int index = Helpers.GetChunkIndex1DFrom3D(pos.x, pos.y, pos.z, pow); return(this[index]); }
public Block GetBlock(ref Vector3Int pos) { int index = Helpers.GetChunkIndex1DFrom3D(pos.x, pos.y, pos.z, pow); return(blockTypes[this[index].Type]); }