public static void DisplacePolygons(Polygon[] polygons, float distance) { // Used for determining if two vertices are the same Polygon.VertexComparerEpsilon vertexComparer = new Polygon.VertexComparerEpsilon(); // Used for determining if two positions or normals are the same Polygon.Vector3ComparerEpsilon vectorComparer = new Polygon.Vector3ComparerEpsilon(); // Group overlapping positions and also track their normals List <List <Vertex> > groupedVertices = new List <List <Vertex> >(); List <List <Vector3> > groupedNormals = new List <List <Vector3> >(); // Maps back from a vertex to the polygon it came from, used for UV calculation Dictionary <Vertex, Polygon> vertexPolygonMappings = new Dictionary <Vertex, Polygon>(); for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) { Vertex[] vertices = polygons[polygonIndex].Vertices; // Group the selected vertices into clusters for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++) { Vertex sourceVertex = vertices[vertexIndex]; vertexPolygonMappings[sourceVertex] = polygons[polygonIndex]; bool added = false; for (int groupIndex = 0; groupIndex < groupedVertices.Count; groupIndex++) { if (groupedVertices[groupIndex].Contains(sourceVertex, vertexComparer)) { groupedVertices[groupIndex].Add(sourceVertex); // Add the normal of the polygon if it hasn't already been added (this prevents issues with two polygons that are coplanar) if (!groupedNormals[groupIndex].Contains(polygons[polygonIndex].Plane.normal, vectorComparer)) { groupedNormals[groupIndex].Add(polygons[polygonIndex].Plane.normal); } added = true; break; } } if (!added) { groupedVertices.Add(new List <Vertex>() { sourceVertex }); groupedNormals.Add(new List <Vector3>() { polygons[polygonIndex].Plane.normal }); } } } List <List <Vector3> > groupedPositions = new List <List <Vector3> >(); List <List <Vector2> > groupedUV = new List <List <Vector2> >(); // Calculate the new positions and UVs, but don't assign them as they must be calculated in one go for (int i = 0; i < groupedVertices.Count; i++) { groupedPositions.Add(new List <Vector3>()); groupedUV.Add(new List <Vector2>()); for (int j = 0; j < groupedVertices[i].Count; j++) { Vector3 position = groupedVertices[i][j].Position; for (int k = 0; k < groupedNormals[i].Count; k++) { position += groupedNormals[i][k] * distance; } Polygon primaryPolygon = vertexPolygonMappings[groupedVertices[i][j]]; Vector2 uv = GeometryHelper.GetUVForPosition(primaryPolygon, position); groupedPositions[i].Add(position); groupedUV[i].Add(uv); } } // Apply the new positions and UVs now that they've all been calculated for (int i = 0; i < groupedVertices.Count; i++) { for (int j = 0; j < groupedVertices[i].Count; j++) { Vertex vertex = groupedVertices[i][j]; vertex.Position = groupedPositions[i][j]; vertex.UV = groupedUV[i][j]; } } // Polygon planes have moved, so recalculate them for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) { polygons[polygonIndex].CalculatePlane(); } }
/// <summary> /// Constructs a polygon from an unordered coplanar set of positions /// </summary> public static Polygon ConstructPolygon(List <Vector3> sourcePositions, bool removeExtraPositions) { List <Vector3> positions; if (removeExtraPositions) { Polygon.Vector3ComparerEpsilon equalityComparer = new Polygon.Vector3ComparerEpsilon(); positions = sourcePositions.Distinct(equalityComparer).ToList(); } else { positions = sourcePositions; } // If positions is smaller than 3 then we can't construct a polygon. This could happen if you try to cut the // tip off a very, very thin brush. While the plane and the brushes would intersect, the actual // cross-sectional area is near zero and too small to create a valid polygon. In this case simply return // null to indicate polygon creation was impossible if (positions.Count < 3) { return(null); } // Find center point, so we can sort the positions around it Vector3 center = positions[0]; for (int i = 1; i < positions.Count; i++) { center += positions[i]; } center *= 1f / positions.Count; if (positions.Count < 3) { Debug.LogError("Position count is below 3, this is probably unhandled"); } // Find the plane UnityEngine.Plane plane = new UnityEngine.Plane(positions[0], positions[1], positions[2]); // Rotation to go from the polygon's plane to XY plane (for sorting) Quaternion cancellingRotation = Quaternion.Inverse(Quaternion.LookRotation(plane.normal)); // Rotate the center point onto the plane too Vector3 rotatedCenter = cancellingRotation * center; // Sort the positions, passing the rotation to put the positions on XY plane and the rotated center point IComparer <Vector3> comparer = new SortVectorsClockwise(cancellingRotation, rotatedCenter); positions.Sort(comparer); // Create the vertices from the positions Vertex[] newPolygonVertices = new Vertex[positions.Count]; for (int i = 0; i < positions.Count; i++) { newPolygonVertices[i] = new Vertex(positions[i], -plane.normal, (cancellingRotation * positions[i]) * 0.5f); } Polygon newPolygon = new Polygon(newPolygonVertices, null, false, false); if (newPolygon.Plane.normal == Vector3.zero) { Debug.LogError("Zero normal found, this leads to invalid polyhedron-point tests"); // hacky // if(removeExtraPositions) // { // Polygon.Vector3ComparerEpsilon equalityComparer = new Polygon.Vector3ComparerEpsilon(); // List<Vector3> testFoo = newPolygonVertices.Select(item => item.Position).Distinct(equalityComparer).ToList(); // } } return(newPolygon); }
/// <summary> /// Translates the specified vertices by a position delta (local to the brush) and updates the UVs /// </summary> /// <param name="brush">Brush from which the vertices belong.</param> /// <param name="specifiedVertices">Specified vertices to be translated.</param> /// <param name="localDelta">Local positional delta.</param> public static void TranslateSpecifiedVertices(Brush brush, List <Vertex> specifiedVertices, Vector3 localDelta) { Polygon.Vector3ComparerEpsilon positionComparer = new Polygon.Vector3ComparerEpsilon(); // Cache the positions as the position of vertices will change while in the for loop List <Vector3> specifiedPositions = specifiedVertices.Select(item => item.Position).ToList(); // So we know which polygons need to have their normals recalculated List <Polygon> affectedPolygons = new List <Polygon>(); Polygon[] polygons = brush.GetPolygons(); for (int i = 0; i < polygons.Length; i++) { Polygon polygon = polygons[i]; int vertexCount = polygon.Vertices.Length; Vector3[] newPositions = new Vector3[vertexCount]; Vector2[] newUV = new Vector2[vertexCount]; for (int j = 0; j < vertexCount; j++) { newPositions[j] = polygon.Vertices[j].Position; newUV[j] = polygon.Vertices[j].UV; } bool polygonAffected = false; for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; if (specifiedPositions.Contains(vertex.Position, positionComparer)) { Vector3 newPosition = vertex.Position + localDelta; newPositions[j] = newPosition; newUV[j] = GeometryHelper.GetUVForPosition(polygon, newPosition); polygonAffected = true; } } if (polygonAffected) { affectedPolygons.Add(polygon); } // Apply all the changes to the polygon for (int j = 0; j < vertexCount; j++) { Vertex vertex = polygon.Vertices[j]; vertex.Position = newPositions[j]; vertex.UV = newUV[j]; } polygon.CalculatePlane(); } if (affectedPolygons.Count > 0) { for (int i = 0; i < affectedPolygons.Count; i++) { affectedPolygons[i].ResetVertexNormals(); } } }