/// <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 static GpcPolygon gpc_polygon_ToPolygon(gpc_polygon gpc_polygon)
        {
            GpcPolygon polygon = new GpcPolygon();

            polygon.NofContours = gpc_polygon.num_contours;
            polygon.ContourIsHole = new bool[polygon.NofContours];
            polygon.Contour = new GpcVertexList[polygon.NofContours];
            short[] holeShort = new short[polygon.NofContours];
            IntPtr ptr = gpc_polygon.hole;
            if (holeShort.Length > 0)
            {
                Marshal.Copy(gpc_polygon.hole, holeShort, 0, polygon.NofContours);
                for (int i = 0; i < polygon.NofContours; i++)
                    polygon.ContourIsHole[i] = (holeShort[i] != 0);

                ptr = gpc_polygon.contour;
                for (int i = 0; i < polygon.NofContours; i++)
                {
                    gpc_vertex_list gpc_vtx_list = (gpc_vertex_list)Marshal.PtrToStructure(ptr, typeof(gpc_vertex_list));
                    polygon.Contour[i] = new GpcVertexList();
                    polygon.Contour[i].NofVertices = gpc_vtx_list.num_vertices;
                    polygon.Contour[i].Vertex = new GpcVertex[polygon.Contour[i].NofVertices];
                    IntPtr ptr2 = gpc_vtx_list.vertex;
                    for (int j = 0; j < polygon.Contour[i].NofVertices; j++)
                    {
                        gpc_vertex gpc_vtx = (gpc_vertex)Marshal.PtrToStructure(ptr2, typeof(gpc_vertex));
                        polygon.Contour[i].Vertex[j].X = gpc_vtx.x;
                        polygon.Contour[i].Vertex[j].Y = gpc_vtx.y;

                        ptr2 = (IntPtr)(((int)ptr2) + Marshal.SizeOf(gpc_vtx));
                    }
                    ptr = (IntPtr)(((int)ptr) + Marshal.SizeOf(gpc_vtx_list));
                }
            }

            return polygon;
        }
        private static gpc_polygon PolygonTo_gpc_polygon(GpcPolygon polygon)
        {
            gpc_polygon gpc_pol = new gpc_polygon();
            gpc_pol.num_contours = polygon.NofContours;

            int[] hole = new int[polygon.NofContours];
            for (int i = 0; i < polygon.NofContours; i++)
                hole[i] = (polygon.ContourIsHole[i] ? 1 : 0);
            if (hole.Length > 0)
            {
                gpc_pol.hole = Marshal.AllocCoTaskMem(polygon.NofContours * Marshal.SizeOf(hole[0]));
                Marshal.Copy(hole, 0, gpc_pol.hole, polygon.NofContours);

                gpc_pol.contour = Marshal.AllocCoTaskMem(polygon.NofContours * Marshal.SizeOf(new gpc_vertex_list()));
                IntPtr ptr = gpc_pol.contour;
                for (int i = 0; i < polygon.NofContours; i++)
                {
                    gpc_vertex_list gpc_vtx_list = new gpc_vertex_list();
                    gpc_vtx_list.num_vertices = polygon.Contour[i].NofVertices;
                    gpc_vtx_list.vertex = Marshal.AllocCoTaskMem(polygon.Contour[i].NofVertices * Marshal.SizeOf(new gpc_vertex()));
                    IntPtr ptr2 = gpc_vtx_list.vertex;
                    for (int j = 0; j < polygon.Contour[i].NofVertices; j++)
                    {
                        gpc_vertex gpc_vtx = new gpc_vertex();
                        gpc_vtx.x = polygon.Contour[i].Vertex[j].X;
                        gpc_vtx.y = polygon.Contour[i].Vertex[j].Y;
                        Marshal.StructureToPtr(gpc_vtx, ptr2, false);
                        ptr2 = (IntPtr)(((int)ptr2) + Marshal.SizeOf(gpc_vtx));
                    }
                    Marshal.StructureToPtr(gpc_vtx_list, ptr, false);
                    ptr = (IntPtr)(((int)ptr) + Marshal.SizeOf(gpc_vtx_list));
                }
            }

            return gpc_pol;
        }
        public static void SavePolygon(string filename, bool writeHoleFlags, GpcPolygon polygon)
        {
            gpc_polygon gpc_polygon = GpcWrapper.PolygonTo_gpc_polygon(polygon);

            IntPtr fp = fopen(filename, "wb");
            gpc_write_polygon(fp, writeHoleFlags ? ((int)1) : ((int)0), ref gpc_polygon);
            fclose(fp);

            GpcWrapper.Free_gpc_polygon(gpc_polygon);
        }
        public static GpcTristrip PolygonToTristrip(GpcPolygon polygon)
        {
            gpc_tristrip gpc_strip = new gpc_tristrip();
            gpc_polygon gpc_pol = GpcWrapper.PolygonTo_gpc_polygon(polygon);
            gpc_polygon_to_tristrip(ref gpc_pol, ref gpc_strip);
            GpcTristrip tristrip = GpcWrapper.gpc_strip_ToTristrip(gpc_strip);

            GpcWrapper.Free_gpc_polygon(gpc_pol);
            GpcWrapper.gpc_free_tristrip(ref gpc_strip);

            return tristrip;
        }
        public static GpcTristrip ClipToTristrip(GpcOperation operation, GpcPolygon subject_polygon, GpcPolygon clip_polygon)
        {
            gpc_tristrip gpc_strip = new gpc_tristrip();
            gpc_polygon gpc_subject_polygon = GpcWrapper.PolygonTo_gpc_polygon(subject_polygon);
            gpc_polygon gpc_clip_polygon = GpcWrapper.PolygonTo_gpc_polygon(clip_polygon);

            gpc_tristrip_clip(operation, ref gpc_subject_polygon, ref gpc_clip_polygon, ref gpc_strip);
            GpcTristrip tristrip = GpcWrapper.gpc_strip_ToTristrip(gpc_strip);

            GpcWrapper.Free_gpc_polygon(gpc_subject_polygon);
            GpcWrapper.Free_gpc_polygon(gpc_clip_polygon);
            GpcWrapper.gpc_free_tristrip(ref gpc_strip);

            return tristrip;
        }
        public static GpcPolygon Clip(GpcOperation operation, GpcPolygon subject_polygon, GpcPolygon clip_polygon)
        {
            gpc_polygon gpc_polygon = new gpc_polygon();
            gpc_polygon gpc_subject_polygon = GpcWrapper.PolygonTo_gpc_polygon(subject_polygon);
            gpc_polygon gpc_clip_polygon = GpcWrapper.PolygonTo_gpc_polygon(clip_polygon);

            gpc_polygon_clip(operation, ref gpc_subject_polygon, ref gpc_clip_polygon, ref gpc_polygon);
            GpcPolygon polygon = GpcWrapper.gpc_polygon_ToPolygon(gpc_polygon);

            GpcWrapper.Free_gpc_polygon(gpc_subject_polygon);
            GpcWrapper.Free_gpc_polygon(gpc_clip_polygon);
            GpcWrapper.gpc_free_polygon(ref gpc_polygon);

            return polygon;
        }
 public GpcTristrip ClipToTristrip(GpcOperation operation, GpcPolygon polygon)
 {
     return GpcWrapper.ClipToTristrip(operation, this, polygon);
 }