Example #1
0
        private static IList <uint> TriangulatePlane(CSGPlane plane, bool insideOut)
        {
            CSGVertexConnectionGraph connectionGraph = new CSGVertexConnectionGraph();

            // Add the initial connections
            Ray[] initialConnectionRays = new Ray[plane.NumVertices];
            for (uint i = 0U; i < plane.NumVertices; ++i)
            {
                connectionGraph.AddConnection(plane[i], plane[(i + 1) % plane.NumVertices]);
                initialConnectionRays[i] = Ray.FromStartAndEndPoint(plane[i].Position, plane[(i + 1) % plane.NumVertices].Position);
            }

            // Add the algorithmic connections
            for (uint a = 0U; a < plane.NumVertices; ++a)
            {
                for (uint b = 0U; b < plane.NumVertices; ++b)
                {
                    if (a == b)
                    {
                        continue;
                    }
                    CSGVertex vertA = plane[a];
                    CSGVertex vertB = plane[b];

                    // Skip existing connections
                    if (connectionGraph.CheckForConnection(vertA, vertB))
                    {
                        continue;
                    }

                    // Is a triangle formable by drawing a connection between A and B?
                    if (!connectionGraph
                        .GetConnections(vertA)
                        .Select(conn => conn.B)
                        .Intersect(connectionGraph.GetConnections(vertB).Select(conn => conn.B))
                        .Any())
                    {
                        continue;
                    }

                    // Does the connection go outside the polygon? If so, it's no good.
                    CSGConnection potentialConnection = new CSGConnection(vertA, vertB);
                    Vector3       connectionCentre    = potentialConnection.Ray.StartPoint + (potentialConnection.Ray.EndPoint.Value - potentialConnection.Ray.StartPoint) / 2f;
                    if (connectionCentre != plane.Center)
                    {
                        Ray connectionToCentreRay = new Ray(connectionCentre, plane.Center - connectionCentre);
                        if ((initialConnectionRays.Count(ray => {
                            Vector3?potentialIntersection = ray.IntersectionWith(connectionToCentreRay);
                            // We check that we're not intersecting the start point because that means this ray is going through a vertex;
                            // so by rejecting intersections through the start point we're not accidentally bumping the number of edge crossings
                            // by 2
                            return(potentialIntersection.HasValue && potentialIntersection != ray.StartPoint);
                        }) & 1) == 0)
                        {
                            continue;
                        }
                    }

                    // Does the connection intersect any other connection? If it does, we would have to create more vertices, so ignore it.
                    if (connectionGraph.GetAllConnections().Any(conn => {
                        Vector3?intersectionPoint = conn.Ray.IntersectionWith(potentialConnection.Ray);
                        if (intersectionPoint.HasValue)
                        {
                            float minDist = Math.Min(
                                Vector3.DistanceSquared(intersectionPoint.Value, potentialConnection.A.Position),
                                Vector3.DistanceSquared(intersectionPoint.Value, potentialConnection.B.Position)
                                );
                            if (minDist > MathUtils.FlopsErrorMargin)
                            {
                                return(true);
                            }
                            return(conn == potentialConnection);
                        }

                        return(false);
                    }))
                    {
                        continue;
                    }

                    // Add this connection.
                    connectionGraph.AddConnection(potentialConnection);
                }
            }

            // Compose the triangles by looking for three-cycles
            List <CSGTriangle> formulatedTriangles = new List <CSGTriangle>();

            for (uint v = 0U; v < plane.NumVertices; ++v)
            {
                CSGVertex sourceVertex = plane[v];
                IEnumerable <CSGConnection> primaryConnections = connectionGraph.GetConnections(sourceVertex, true);

                foreach (CSGConnection primary in primaryConnections)
                {
                    IEnumerable <CSGConnection> secondaryConnections = connectionGraph.GetConnections(primary.B, true)
                                                                       .Where(sConn => sConn.B != sourceVertex && primaryConnections.Any(pConn => pConn.B == sConn.B));

                    // Now we have a set of triangles (source -> pConn.B -> sConn.B), let's find the ones we haven't already
                    // added and add those!
                    foreach (CSGConnection secondary in secondaryConnections)
                    {
                        CSGTriangle potentialTriangle = new CSGTriangle(v, plane.IndexOf(secondary.A), plane.IndexOf(secondary.B));
                        if (!formulatedTriangles.Contains(potentialTriangle))
                        {
                            formulatedTriangles.Add(potentialTriangle);
                        }
                    }
                }
            }

            List <uint> result = new List <uint>();

            foreach (CSGTriangle triangle in formulatedTriangles)
            {
                Vector3 aPos           = plane[triangle.A].Position;
                Vector3 bPos           = plane[triangle.B].Position;
                Vector3 triCentre      = (aPos + bPos + plane[triangle.C].Position) / 3f;
                bool    triIsClockwise = plane.Plane.Normal.Dot(Vector3.Cross(aPos - triCentre, bPos - triCentre)) > 0f;
                if (insideOut)
                {
                    triIsClockwise = !triIsClockwise;
                }

                result.Add(triangle.A);

                if (triIsClockwise)
                {
                    result.Add(triangle.B);
                    result.Add(triangle.C);
                }
                else
                {
                    result.Add(triangle.C);
                    result.Add(triangle.B);
                }
            }

            return(result);
        }
 public void AddConnection(CSGConnection c)
 {
     AddConnection(c.A, c.B);
 }