Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }
Пример #5
0
        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()
                    };
                }
            }
        }
Пример #6
0
        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);
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
        }
Пример #11
0
        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);
        }