private void ConnectMinimizingVertices(Chunk c)
        {
            Dictionary <Vector3I, short>          positionToIndex = _positionToIndex[c];
            List <VertexPositionNormalColorLight> vertices        = _vertices[c];
            List <VertexPositionNormalColorLight> meshVertices    = _meshVertices[c];
            List <short> meshIndices = _indices[c];

            foreach (Edge e in _intersectingEdges[c])
            {
                Direction d = e.FaceDirection;

                short[] indices = new short[6];

                Vector3I[] cubes = e.GetCubePositions();

                bool invalid = false;

                HashSet <Edge>     borderEdges           = _borderEdges[c];
                HashSet <Vector3I> neededBorderPositions = _neededBorderPositions[c];

                for (int i = 0; i < cubes.Length; i++)
                {
                    if (cubes[i].X < 0 || cubes[i].Y < 0 || cubes[i].Z < 0)
                    {
                        invalid = true;
                        break;
                    }

                    if (cubes[i].X > c.Width - 1 || cubes[i].Y > c.Height - 1 || cubes[i].Z > c.Length - 1)
                    {
                        borderEdges.Add(e);

                        neededBorderPositions.Add(cubes[i]);

                        invalid = true;
                    }
                }

                if (invalid)
                {
                    continue;
                }

                // used to determine which config the face is in; this is used to know which indices refer to the same vertex below
                bool indicesConfig = false;

                // TODO: possible degenerate triangles (0 area)
                switch (d)
                {
                case Direction.XDecreasing:
                case Direction.ZDecreasing:
                case Direction.YIncreasing:
                    indices[0] = positionToIndex[cubes[0]];
                    indices[1] = positionToIndex[cubes[1]];
                    indices[2] = positionToIndex[cubes[3]];
                    indices[3] = positionToIndex[cubes[0]];
                    indices[4] = positionToIndex[cubes[3]];
                    indices[5] = positionToIndex[cubes[2]];

                    indicesConfig = true;     // duplicates at 0,3 and 2,4
                    break;

                case Direction.XIncreasing:
                case Direction.ZIncreasing:
                case Direction.YDecreasing:
                    indices[0] = positionToIndex[cubes[0]];
                    indices[1] = positionToIndex[cubes[3]];
                    indices[2] = positionToIndex[cubes[1]];
                    indices[3] = positionToIndex[cubes[0]];
                    indices[4] = positionToIndex[cubes[2]];
                    indices[5] = positionToIndex[cubes[3]];

                    indicesConfig = false;     // duplicates at 0,3, and 1,5
                    break;
                }

                short index0 = positionToIndex[cubes[0]];
                short index1 = positionToIndex[cubes[1]];
                short index2 = positionToIndex[cubes[2]];
                short index3 = positionToIndex[cubes[3]];

                // triangle 1
                Vector3 point0 = vertices[indices[0]].Position;
                Vector3 point1 = vertices[indices[1]].Position;
                Vector3 point2 = vertices[indices[2]].Position;

                Vector3 v01 = point1 - point0;
                Vector3 v12 = point2 - point1;

                Vector3 normal1 = Vector3.Cross(v12, v01);
                normal1.Normalize();

                if (Single.IsNaN(normal1.X) || Single.IsNaN(normal1.Y) || Single.IsNaN(normal1.Z))
                {
                    normal1 = Vector3.Zero; // TODO: this is a hack; think it through later
                }
                // triangle 2
                Vector3 point3 = vertices[indices[3]].Position;
                Vector3 point4 = vertices[indices[4]].Position;
                Vector3 point5 = vertices[indices[5]].Position;

                Vector3 v34 = point4 - point3;
                Vector3 v45 = point5 - point4;

                Vector3 normal2 = Vector3.Cross(v45, v34);
                normal2.Normalize();

                if (Single.IsNaN(normal2.X) || Single.IsNaN(normal2.Y) || Single.IsNaN(normal2.Z))
                {
                    normal2 = Vector3.Zero; // TODO: this is a hack; think it through later
                }
                //_normalVertices.Add(new VertexPositionColor(point1, Color.White));
                //_normalVertices.Add(new VertexPositionColor(point1 + normal1, Color.White));
                //_normalVertices.Add(new VertexPositionColor(point2, Color.Black));
                //_normalVertices.Add(new VertexPositionColor(point2 + normal2, Color.Black));

                _triangles += 2;

                VertexPositionNormalColorLight vertex0 = vertices[indices[0]];
                VertexPositionNormalColorLight vertex1 = vertices[indices[1]];
                VertexPositionNormalColorLight vertex2 = vertices[indices[2]];
                VertexPositionNormalColorLight vertex3 = vertices[indices[3]];
                VertexPositionNormalColorLight vertex4 = vertices[indices[4]];
                VertexPositionNormalColorLight vertex5 = vertices[indices[5]];

                if (indicesConfig)
                {
                    vertex0.Normal += normal1 + normal2;
                    vertex1.Normal += normal1;
                    vertex2.Normal += normal1 + normal2;
                    vertex5.Normal += normal2;

                    AddIndices(meshIndices, meshVertices.Count, 0, 1, 2, 0, 2, 3);
                    AddVertices(meshVertices, vertex0, vertex1, vertex2, vertex5);
                }
                else
                {
                    vertex0.Normal += normal1 + normal2;
                    vertex1.Normal += normal1 + normal2;
                    vertex2.Normal += normal1;
                    vertex4.Normal += normal2;

                    AddIndices(meshIndices, meshVertices.Count, 0, 1, 2, 0, 3, 1);
                    AddVertices(meshVertices, vertex0, vertex1, vertex2, vertex4);
                }
            }
        }
        private void BuildBuffers(Chunk c)
        {
            VertexPositionNormalColorLight[] vertices = new VertexPositionNormalColorLight[_meshVertices[c].Count];
            short[] indices = new short[_indices[c].Count];

            _meshVertices[c].CopyTo(vertices);
            _indices[c].CopyTo(indices);

            NormalizeNormals(c, vertices);

            VertexPositionColor[] normal = new VertexPositionColor[_normalVertices[c].Count];
            _normalVertices[c].CopyTo(normal);

            Vector3[] verticesPos = new Vector3[vertices.Length];
            int[]     indices32   = new int[indices.Length];

            for (int i = 0; i < vertices.Length; i++)
            {
                verticesPos[i] = vertices[i].Position;
            }

            for (int i = 0; i < indices.Length; i++)
            {
                indices32[i] = indices[i];
            }

            if (MeshBuilt != null)
            {
                MeshBuilt(c, new MeshBuildEventArgs(verticesPos, indices32));
            }

            lock (c.GraphicsSync)
            {
                if (c.VertexBuffer != null)
                {
                    c.VertexBuffer.Dispose();
                }
                if (c.IndexBuffer != null)
                {
                    c.IndexBuffer.Dispose();
                }
                if (c.NormalsVertexBuffer != null)
                {
                    c.NormalsVertexBuffer.Dispose();
                }

                if (_vertices[c].Count <= 0 || _indices[c].Count <= 0)
                {
                    c.VertexBuffer        = null;
                    c.IndexBuffer         = null;
                    c.NormalsVertexBuffer = null;
                }
                else
                {
                    c.VertexBuffer        = new DynamicVertexBuffer(_device, typeof(VertexPositionNormalColorLight), vertices.Length, BufferUsage.WriteOnly);
                    c.IndexBuffer         = new DynamicIndexBuffer(_device, IndexElementSize.SixteenBits, indices.Length, BufferUsage.WriteOnly);
                    c.NormalsVertexBuffer = new DynamicVertexBuffer(_device, typeof(VertexPositionColor), normal.Length, BufferUsage.WriteOnly);

                    c.VertexBuffer.SetData <VertexPositionNormalColorLight>(vertices);
                    c.IndexBuffer.SetData <short>(indices);
                    c.NormalsVertexBuffer.SetData <VertexPositionColor>(normal);
                }
            }

            //_vertices[c].Clear();
            _normalVertices[c].Clear();
            _indices[c].Clear();
            _intersectingEdges[c].Clear();
            //_positionToIndex[c].Clear();
        }
        public void PostProcess(Chunk c)
        {
            /*
             * BuildBuffers(c);
             * c.MarkDataInSync();
             *
             * return;*/

            // after we build the chunk's vertices, we need to stitch the borders.

            // each chunk only stiches borders with its XPositive, YPositive, and ZPositive neighbor chunks. (if they aren't null)
            // this ensures that we don't create more quads than necessary.

            List <VertexPositionNormalColorLight> vertices     = _vertices[c];
            List <VertexPositionNormalColorLight> meshVertices = _meshVertices[c];
            List <short> meshIndices = _indices[c];
            Dictionary <Vector3I, short> positionToIndex       = _positionToIndex[c];
            HashSet <Vector3I>           neededBorderPositions = _neededBorderPositions[c];

            foreach (Vector3I pos in neededBorderPositions)
            {
                if (!_world.InBounds((c.Position + pos).ToVector3()))
                {
                    continue;
                }

                Chunk otherChunk = _world.ChunkAt(c.Position.X + pos.X, c.Position.Y + pos.Y, c.Position.Z + pos.Z);

                Vector3I otherChunkLocalPos = new Vector3I(pos.X % otherChunk.Width, pos.Y % otherChunk.Height, pos.Z % otherChunk.Length);

                vertices.Add(_vertices[otherChunk][_positionToIndex[otherChunk][otherChunkLocalPos]]);
                positionToIndex.Add(pos, (short)(vertices.Count - 1));
            }

            foreach (Edge e in _borderEdges[c])
            {
                Direction d = e.FaceDirection;

                short[] indices = new short[6];

                Vector3I[] cubes = e.GetCubePositions();

                bool invalid = false;

                for (int i = 0; i < cubes.Length; i++)
                {
                    if (cubes[i].X < 0 || cubes[i].Y < 0 || cubes[i].Z < 0 || !_world.InBounds((c.Position + cubes[i]).ToVector3()))
                    {
                        invalid = true;
                        break;
                    }
                }

                if (invalid)
                {
                    continue;
                }

                // used to determine which config the face is in; this is used to know which indices refer to the same vertex below
                bool indicesConfig = false;

                // TODO: possible degenerate triangles (0 area)
                switch (d)
                {
                case Direction.XDecreasing:
                case Direction.ZDecreasing:
                case Direction.YIncreasing:
                    indices[0]    = positionToIndex[cubes[0]];
                    indices[1]    = positionToIndex[cubes[1]];
                    indices[2]    = positionToIndex[cubes[3]];
                    indices[3]    = positionToIndex[cubes[0]];
                    indices[4]    = positionToIndex[cubes[3]];
                    indices[5]    = positionToIndex[cubes[2]];
                    indicesConfig = true;
                    break;

                case Direction.XIncreasing:
                case Direction.ZIncreasing:
                case Direction.YDecreasing:
                    indices[0]    = positionToIndex[cubes[0]];
                    indices[1]    = positionToIndex[cubes[3]];
                    indices[2]    = positionToIndex[cubes[1]];
                    indices[3]    = positionToIndex[cubes[0]];
                    indices[4]    = positionToIndex[cubes[2]];
                    indices[5]    = positionToIndex[cubes[3]];
                    indicesConfig = false;
                    break;
                }

                short index0 = positionToIndex[cubes[0]];
                short index1 = positionToIndex[cubes[1]];
                short index2 = positionToIndex[cubes[2]];
                short index3 = positionToIndex[cubes[3]];

                // triangle 1
                Vector3 point0 = vertices[indices[0]].Position;
                Vector3 point1 = vertices[indices[1]].Position;
                Vector3 point2 = vertices[indices[2]].Position;

                Vector3 v01 = point1 - point0;
                Vector3 v12 = point2 - point1;

                Vector3 normal1 = Vector3.Cross(v12, v01);
                normal1.Normalize();

                if (Single.IsNaN(normal1.X) || Single.IsNaN(normal1.Y) || Single.IsNaN(normal1.Z))
                {
                    normal1 = Vector3.Zero; // TODO: this is a hack; think it through later
                }
                // triangle 2
                Vector3 point3 = vertices[indices[3]].Position;
                Vector3 point4 = vertices[indices[4]].Position;
                Vector3 point5 = vertices[indices[5]].Position;

                Vector3 v34 = point4 - point3;
                Vector3 v45 = point5 - point4;

                Vector3 normal2 = Vector3.Cross(v45, v34);
                normal2.Normalize();

                if (Single.IsNaN(normal2.X) || Single.IsNaN(normal2.Y) || Single.IsNaN(normal2.Z))
                {
                    normal2 = Vector3.Zero; // TODO: this is a hack; think it through later
                }
                _triangles += 2;

                VertexPositionNormalColorLight vertex0 = vertices[indices[0]];
                VertexPositionNormalColorLight vertex1 = vertices[indices[1]];
                VertexPositionNormalColorLight vertex2 = vertices[indices[2]];
                VertexPositionNormalColorLight vertex3 = vertices[indices[3]];
                VertexPositionNormalColorLight vertex4 = vertices[indices[4]];
                VertexPositionNormalColorLight vertex5 = vertices[indices[5]];

                if (indicesConfig)
                {
                    vertex0.Normal += normal1 + normal2;
                    vertex1.Normal += normal1;
                    vertex2.Normal += normal1 + normal2;
                    vertex5.Normal += normal2;

                    AddIndices(meshIndices, meshVertices.Count, 0, 1, 2, 0, 2, 3);
                    AddVertices(meshVertices, vertex0, vertex1, vertex2, vertex5);
                }
                else
                {
                    vertex0.Normal += normal1 + normal2;
                    vertex1.Normal += normal1 + normal2;
                    vertex2.Normal += normal1;
                    vertex4.Normal += normal2;

                    AddIndices(meshIndices, meshVertices.Count, 0, 1, 2, 0, 3, 1);
                    AddVertices(meshVertices, vertex0, vertex1, vertex2, vertex4);
                }
            }

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            BuildBuffers(c);
            sw.Stop();
            //Console.WriteLine("BuildBuffers(): {0}ms", sw.ElapsedMilliseconds);

            c.MarkDataInSync();
        }