public ForgeContentsRenderer(BlockPos pos, ICoreClientAPI capi)
        {
            this.pos  = pos;
            this.capi = capi;

            metals = capi.Assets.TryGet("worldproperties/block/metal.json").ToObject <MetalProperty>();

            Block block = capi.World.GetBlock(new AssetLocation("forge"));

            coaltexpos  = capi.BlockTextureAtlas.GetPosition(block, "coal");
            embertexpos = capi.BlockTextureAtlas.GetPosition(block, "ember");

            MeshData emberMesh = QuadMeshUtil.GetCustomQuadHorizontal(3 / 16f, 0, 3 / 16f, 10 / 16f, 10 / 16f, 255, 255, 255, 255);

            for (int i = 0; i < emberMesh.Uv.Length; i += 2)
            {
                emberMesh.Uv[i + 0] = embertexpos.x1 + emberMesh.Uv[i + 0] * 32f / AtlasSize;
                emberMesh.Uv[i + 1] = embertexpos.y1 + emberMesh.Uv[i + 1] * 32f / AtlasSize;
            }
            emberMesh.Flags = new int[] { 128, 128, 128, 128 };

            MeshData coalMesh = QuadMeshUtil.GetCustomQuadHorizontal(3 / 16f, 0, 3 / 16f, 10 / 16f, 10 / 16f, 255, 255, 255, 255);

            for (int i = 0; i < coalMesh.Uv.Length; i += 2)
            {
                coalMesh.Uv[i + 0] = coaltexpos.x1 + coalMesh.Uv[i + 0] * 32f / AtlasSize;;
                coalMesh.Uv[i + 1] = coaltexpos.y1 + coalMesh.Uv[i + 1] * 32f / AtlasSize;;
            }


            emberQuadRef = capi.Render.UploadMesh(emberMesh);
            coalQuadRef  = capi.Render.UploadMesh(coalMesh);
        }
        public void RegenMesh()
        {
            ICoreClientAPI capi = api as ICoreClientAPI;

            mesh = new MeshData(24, 36, false).WithTints().WithRenderpasses();


            float subPixelPadding = capi.BlockTextureAtlas.SubPixelPadding;

            MeshData[] meshesByFace = new MeshData[6];



            // North
            meshesByFace[0] = QuadMeshUtil.GetCustomQuad(0, 0, 0, 1 / 16f, 1 / 16f, 255, 255, 255, 255);
            meshesByFace[0].Rotate(new Vec3f(1 / 32f, 1 / 32f, 1 / 32f), 0, GameMath.PI, 0);
            meshesByFace[0].Translate(0, 0, -1 / 16f);

            // East
            meshesByFace[1] = QuadMeshUtil.GetCustomQuad(0, 0, 0, 1 / 16f, 1 / 16f, 255, 255, 255, 255);
            meshesByFace[1].Rotate(new Vec3f(1 / 32f, 1 / 32f, 1 / 32f), 0, GameMath.PIHALF, 0);
            meshesByFace[1].Translate(1 / 16f, 0, 0);

            // South
            meshesByFace[2] = QuadMeshUtil.GetCustomQuad(0, 0, 1 / 16f, 1 / 16f, 1 / 16f, 255, 255, 255, 255);

            // West
            meshesByFace[3] = QuadMeshUtil.GetCustomQuad(0, 0, 0, 1 / 16f, 1 / 16f, 255, 255, 255, 255);
            meshesByFace[3].Rotate(new Vec3f(1 / 32f, 1 / 32f, 1 / 32f), 0, -GameMath.PIHALF, 0);
            meshesByFace[3].Translate(-1 / 16f, 0, 0);

            // Up
            meshesByFace[4] = QuadMeshUtil.GetCustomQuadHorizontal(0, 1 / 16f, 0, 1 / 16f, 1 / 16f, 255, 255, 255, 255);
            meshesByFace[4].Rotate(new Vec3f(1 / 32f, 1 / 32f, 1 / 32f), GameMath.PI, 0, 0);
            meshesByFace[4].Translate(0, 1 / 16f, 0);

            // Down
            meshesByFace[5] = QuadMeshUtil.GetCustomQuadHorizontal(0, 0, 0, 1 / 16f, 1 / 16f, 255, 255, 255, 255);


            float[] sideShadings = CubeMeshUtil.DefaultBlockSideShadingsByFacing;

            for (int i = 0; i < meshesByFace.Length; i++)
            {
                MeshData mesh = meshesByFace[i];
                mesh.Rgba = new byte[16];
                mesh.Rgba.Fill((byte)(255 * sideShadings[i]));

                mesh.rgba2 = new byte[16];
                mesh.rgba2.Fill((byte)(255 * sideShadings[i]));
                mesh.Flags = new int[4];
                mesh.Flags.Fill(0);
                mesh.RenderPasses    = new int[1];
                mesh.RenderPassCount = 1;
                mesh.Tints           = new int[1];
                mesh.TintsCount      = 1;
                mesh.XyzFaces        = new int[] { i };
                mesh.XyzFacesCount   = 1;

                TextureAtlasPosition tpos = capi.BlockTextureAtlas.GetPosition(block, BlockFacing.ALLFACES[i].Code);
                for (int j = 0; j < mesh.Uv.Length; j++)
                {
                    mesh.Uv[j] = (j % 2 > 0 ? tpos.y1 : tpos.x1) + mesh.Uv[j] * 32f / capi.BlockTextureAtlas.Size - subPixelPadding;
                }
            }

            MeshData[] voxelMeshesOffset = new MeshData[6];
            for (int i = 0; i < meshesByFace.Length; i++)
            {
                voxelMeshesOffset[i] = meshesByFace[i].Clone();
            }


            // North: Negative Z
            // East: Positive X
            // South: Positive Z
            // West: Negative X

            bool[] sideVisible = new bool[6];

            int[] coords = new int[3];

            int[][] coordIndexByFace = new int[][] {
                // N
                new int[] { 0, 1 },
                // E
                new int[] { 2, 1 },
                // S
                new int[] { 0, 1 },
                // W
                new int[] { 2, 1 },
                // U
                new int[] { 0, 2 },
                // D
                new int[] { 0, 2 }
            };

            for (int x = 0; x < 16; x++)
            {
                coords[0] = x;

                for (int y = 0; y < 16; y++)
                {
                    coords[1] = y;

                    for (int z = 0; z < 16; z++)
                    {
                        if (!Voxels[x, y, z])
                        {
                            continue;
                        }

                        coords[2] = z;

                        float px = x / 16f;
                        float py = y / 16f;
                        float pz = z / 16f;

                        sideVisible[0] = z == 0 || !Voxels[x, y, z - 1];
                        sideVisible[1] = x == 15 || !Voxels[x + 1, y, z];
                        sideVisible[2] = z == 15 || !Voxels[x, y, z + 1];
                        sideVisible[3] = x == 0 || !Voxels[x - 1, y, z];
                        sideVisible[4] = y == 15 || !Voxels[x, y + 1, z];
                        sideVisible[5] = y == 0 || !Voxels[x, y - 1, z];

                        for (int f = 0; f < 6; f++)
                        {
                            if (!sideVisible[f])
                            {
                                continue;
                            }

                            MeshData facerefmesh    = meshesByFace[f];
                            MeshData faceoffsetmesh = voxelMeshesOffset[f];

                            for (int i = 0; i < facerefmesh.xyz.Length; i += 3)
                            {
                                faceoffsetmesh.xyz[i]     = px + facerefmesh.xyz[i];
                                faceoffsetmesh.xyz[i + 1] = py + facerefmesh.xyz[i + 1];
                                faceoffsetmesh.xyz[i + 2] = pz + facerefmesh.xyz[i + 2];
                            }

                            float offsetX = (coords[coordIndexByFace[f][0]] * 2f) / capi.BlockTextureAtlas.Size;
                            float offsetZ = (coords[coordIndexByFace[f][1]] * 2f) / capi.BlockTextureAtlas.Size;

                            for (int i = 0; i < facerefmesh.Uv.Length; i += 2)
                            {
                                faceoffsetmesh.Uv[i]     = facerefmesh.Uv[i] + offsetX;
                                faceoffsetmesh.Uv[i + 1] = facerefmesh.Uv[i + 1] + offsetZ;
                            }

                            mesh.AddMeshData(faceoffsetmesh);
                        }
                    }
                }
            }
        }