/// <summary>
        /// Can build side: Input1: Block at side, Input2: Base block
        /// </summary>
        public static List<CustomBlockData> GenerateModel(List<BoundingBox> boxes, BlockSource source, Point3 blockPos, bool removeCoveredFaces, IGeometryGeneratorSource i)
        {
            BlockData currentBlock = source.GetData(blockPos);

            Vector3[] norms = new Vector3[] { new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1) };

            Dictionary<Vector3, Dictionary<float, List<Face>>> faces = new Dictionary<Vector3, Dictionary<float, List<Face>>>();
            //Vector3 key: normal
            //float key: value of normal component

            //Foreach bounding box
            foreach (BoundingBox bb in boxes)
            {
                //Foreach normal (Minecraft normals)
                foreach (Vector3 normal in norms)
                {
                    //Get the face for the normal
                    Face face = GetFace(bb, normal);
                    face.TextureTag = bb.TextureTag;

                    //Store it using the important Normal component
                    float val = face.GetNormalValue();

                    if (float.IsNaN(val))
                        continue;

                    Dictionary<float, List<Face>> dict;
                    if (faces.ContainsKey(normal))
                    {
                        dict = faces[normal];
                    }
                    else
                    {
                        dict = new Dictionary<float, List<Face>>();
                        faces.Add(normal, dict);
                    }

                    if (dict.ContainsKey(val))
                        dict[val].Add(face);
                    else
                        dict.Add(val, new List<Face>() { face });
                }
            }

            List<CustomBlockData> datas = new List<CustomBlockData>();

            //Foreach normal
            foreach (KeyValuePair<Vector3, Dictionary<float, List<Face>>> pair in faces)
            {
                //Foreach normal component
                foreach (KeyValuePair<float, List<Face>> pair2 in pair.Value)
                {
                    //Check if we should build the side
                    if(pair2.Key == 0 && (pair.Key.X == -1 || pair.Key.Y == -1 || pair.Key.Z == -1)) //- sides
                    {
                        Point3 dir3 = pair.Key.ToPoint3();
                        Point3 atSide = blockPos + dir3;

                        BlockData atSideBlock = source.GetData(atSide);
                        if (!i.CanBuildSide(currentBlock, atSideBlock, blockPos, atSide))
                        {
                            continue;
                        }
                    }

                    if (pair2.Key == 1 && (pair.Key.X == 1 || pair.Key.Y == 1 || pair.Key.Z == 1)) //+ sides
                    {
                        Point3 dir3 = pair.Key.ToPoint3();
                        Point3 atSide = blockPos + dir3;

                        BlockData atSideBlock = source.GetData(atSide);
                        if (!i.CanBuildSide(currentBlock, atSideBlock, blockPos, atSide))
                        {
                            continue;
                        }
                    }

                    //pair2.Value contains the faces of the current Normal and Normal component value
                    //Let's calculate what we can see:
                    GpcPolygon polygon = new GpcPolygon();
                    foreach(Face face in pair2.Value)
                    {
                        GpcPolygon addPoly = new GpcPolygon();
                        addPoly.AddContour(new GpcVertexList(face.ConvertToPointList()), false);

                        polygon = polygon.Clip(GpcOperation.Union, addPoly);
                    }

                    //Remove the invisible parts
                    Vector3 inverseNormal = -pair.Key;
                    if (faces.ContainsKey(inverseNormal) && removeCoveredFaces)
                    {
                        if (faces[inverseNormal].ContainsKey(pair2.Key)) //There are faces, which make this face invisible
                        {
                            GpcPolygon invisible = new GpcPolygon();
                            foreach (Face face2 in faces[inverseNormal][pair2.Key])
                            {
                                GpcPolygon addPoly = new GpcPolygon();
                                addPoly.AddContour(new GpcVertexList(face2.ConvertToPointList()), false);

                                invisible = invisible.Clip(GpcOperation.Union, addPoly);
                            }

                            polygon = polygon.Clip(GpcOperation.Difference, invisible); //Remove the invisible parts
                        }
                    }

                    //Create the face
                    if (polygon.NofContours == 0)
                        continue;

                    foreach (GpcVertexList polys in polygon.Contour)
                    {
                        List<PolygonPoint> points = new List<PolygonPoint>();
                        foreach (GpcVertex vert in polys.Vertex)
                        {
                            points.Add(new PolygonPoint(vert.X, vert.Y));
                        }
                        Polygon triangulatorPoly = new Polygon(points);

                        //MAGIC :D
                        Triangulator.Triangulate(triangulatorPoly);

                        foreach (DelaunayTriangle tri in triangulatorPoly.Triangles)
                        {
                            CustomBlockData bd = new CustomBlockData();

                            bd.IsOneTriangle = true;
                            bd.Texture = i.GetTexture(pair2.Value[0]);

                            bd.Vertex1 = ConvertToVertexPosition(tri.Points[0], pair.Key, pair2.Key);
                            bd.Vertex2 = ConvertToVertexPosition(tri.Points[1], pair.Key, pair2.Key);
                            bd.Vertex3 = ConvertToVertexPosition(tri.Points[2], pair.Key, pair2.Key);

                            Vector2[] uvs = new Vector2[]{
                                new Vector2(tri.Points[0].Xf, tri.Points[0].Yf),
                                new Vector2(tri.Points[1].Xf, tri.Points[1].Yf),
                                new Vector2(tri.Points[2].Xf, tri.Points[2].Yf)
                            };

                            uvs = i.GetUVsForTriangle(uvs, pair2.Value[0]);

                            bd.UV1 = uvs[0];
                            bd.UV2 = uvs[1];
                            bd.UV3 = uvs[2];

                            bd.Normal = pair.Key;

                            datas.Add(bd);
                        }
                    }
                }
            }

            return datas;
        }
        private void WriteCustomData(CustomBlockData range, ref Dictionary<string, DataSet> datas)
        {
            DataSet foundSet = null;

            if (datas.ContainsKey(range.Texture))
                foundSet = datas[range.Texture];
            else
            {
                foundSet = new DataSet();
                foundSet.BaseData = range.Source;
                foundSet.Texture = range.Texture;

                datas.Add(range.Texture, foundSet);
            }

            Vector3 normal = range.Normal;

            //BlockSide side = Block.GetSideFromNormal(normal);
            //Block bl = Block.Blocks[range.Source.GetGlobalID()];
            //byte sideByte = 6;
            //DataSet foundSet = null;
            //sideByte = (byte)Block.GetSideInt(side);
            //if (bl.UsesOneTexture)
            //{
            //    side = BlockSide.AllSame;
            //    sideByte = (byte)Block.GetSideInt(side);
            //}
            //else
            //{
            //    string tex = range.Texture;//bl.GetTextureForSide(side, range.Source.Metadata);
            //    int[] ids = Find(bl.GetTextures(range.Source.Metadata), tex);

            //    DataSet[] ds = new DataSet[ids.Length];

            //    KeyStruct key = new KeyStruct()
            //    {
            //        ID = range.Source.ID,
            //        Metadata = range.Source.Metadata,
            //        SideByte = sideByte,
            //    };

            //    for (int s = 0; s < ids.Length; s++)
            //    {
            //        KeyStruct k = new KeyStruct()
            //        {
            //            ID = range.Source.ID,
            //            Metadata = range.Source.Metadata,
            //            SideByte = (byte)ids[s]
            //        };

            //        if (datas.ContainsKey(k) && key != k)
            //        {
            //            ds[s] = datas[k];
            //        }
            //    }

            //    for (int e = 0; e < ds.Length; e++)
            //        if (ds[e] != null)
            //        {
            //            foundSet = ds[e];
            //            break;
            //        }
            //}

            //if (foundSet == null)
            //{
            //    KeyStruct key = new KeyStruct()
            //    {
            //        ID = range.Source.ID,
            //        Metadata = range.Source.Metadata,
            //        SideByte = sideByte
            //    };

            //    if (!datas.ContainsKey(key))
            //    {
            //        datas.Add(key, new DataSet());
            //        datas[key].BaseData = range.Source;
            //        datas[key].SideByte = sideByte;

            //        foundSet = datas[key];
            //    }
            //    else
            //    {
            //        foundSet = datas[key];
            //    }
            //}

            //tri1: 1,2,3
            //tri2: 3,4,1

            //Vector3 tri1Norm = Vector3.Cross((range.Vertex2 - range.Vertex1), (range.Vertex3 - range.Vertex1));
            //Vector3 tri2Norm = Vector3.Cross((range.Vertex3 - range.Vertex1), (range.Vertex4 - range.Vertex1));
            Vector3 tri1Norm = normal;
            Vector3 tri2Norm = normal;

            if (range.TriFlip)// || (!range.TriFlip && (normal.X < 0 || normal.Y < 0 || normal.Z < 0)))
            {
                WriteTriangle(foundSet, range.Vertex3, range.Vertex2, range.Vertex1, range.UV3, range.UV2, range.UV1, tri1Norm);
                if (!range.IsOneTriangle)
                {
                    WriteTriangle(foundSet, range.Vertex1, range.Vertex4, range.Vertex3, range.UV1, range.UV4, range.UV3, tri2Norm);
                }
            }
            else
            {
                WriteTriangle(foundSet, range.Vertex1, range.Vertex2, range.Vertex3, range.UV1, range.UV2, range.UV3, tri1Norm);
                if (!range.IsOneTriangle)
                {
                    WriteTriangle(foundSet, range.Vertex3, range.Vertex4, range.Vertex1, range.UV3, range.UV4, range.UV1, tri2Norm);
                }
            }

            if (range.DoubleSided)
            {
                range.DoubleSided = false;
                range.Normal = -range.Normal;
                WriteCustomData(range, ref datas);
            }
        }