public static void Extrude_Face_MultipleTimes_CreatesValidGeometry([ValueSource("shapeTypes")] ShapeType shape) { var mesh = ShapeGenerator.CreateShape(shape); try { int initialVertexCount = mesh.vertexCount; UnityEngine.ProBuilder.Face face = mesh.facesInternal[m_Random.Next(0, mesh.facesInternal.Length)]; mesh.Extrude(new UnityEngine.ProBuilder.Face[] { face }, ExtrudeMethod.FaceNormal, 1f); mesh.ToMesh(); mesh.Refresh(); LogAssert.NoUnexpectedReceived(); TestUtility.AssertMeshAttributesValid(mesh.mesh); Assert.AreEqual(initialVertexCount + face.edgesInternal.Length * 4, mesh.vertexCount); initialVertexCount = mesh.vertexCount; mesh.Extrude(new UnityEngine.ProBuilder.Face[] { face }, ExtrudeMethod.VertexNormal, 1f); mesh.ToMesh(); mesh.Refresh(); LogAssert.NoUnexpectedReceived(); TestUtility.AssertMeshAttributesValid(mesh.mesh); Assert.AreEqual(initialVertexCount + face.edgesInternal.Length * 4, mesh.vertexCount); initialVertexCount = mesh.vertexCount; mesh.Extrude(new UnityEngine.ProBuilder.Face[] { face }, ExtrudeMethod.IndividualFaces, 1f); mesh.ToMesh(); mesh.Refresh(); LogAssert.NoUnexpectedReceived(); TestUtility.AssertMeshAttributesValid(mesh.mesh); Assert.AreEqual(initialVertexCount + face.edgesInternal.Length * 4, mesh.vertexCount); } catch (Exception e) { Debug.LogError(e.ToString()); } finally { if (mesh != null) { Object.DestroyImmediate(mesh.gameObject); } } }
/// <summary> /// Deep copy constructor. /// </summary> /// <param name="other">The Face from which to copy properties.</param> public Face(Face other) { CopyFrom(other); }
public void SetSingleFace(Face face) { faces.Clear(); faces.Add(face); }
internal static void Unwrap(ProBuilderMesh mesh, Face face, Vector3 projection = default) { Projection.PlanarProject(mesh, face, projection != Vector3.zero ? projection : Vector3.zero); ApplyUVSettings(mesh.texturesInternal, face.distinctIndexesInternal, face.uv); }
/// <summary> /// Pick faces contained within rect. /// </summary> /// <param name="cam"></param> /// <param name="rect">Rect is in GUI space, where 0,0 is top left of screen, width = cam.pixelWidth / pointsPerPixel.</param> /// <param name="selectable"></param> /// <param name="options"></param> /// <param name="pixelsPerPoint">Scale the render texture to match rect coordinates. Generally you'll just pass in EditorGUIUtility.pixelsPerPoint.</param> /// <returns></returns> public static Dictionary <ProBuilderMesh, HashSet <Face> > PickFacesInRect( Camera cam, Rect rect, IList <ProBuilderMesh> selectable, PickerOptions options, float pixelsPerPoint = 1f) { if (options.depthTest && options.rectSelectMode == RectSelectMode.Partial) { return(SelectionPickerRenderer.PickFacesInRect( cam, rect, selectable, (int)(cam.pixelWidth / pixelsPerPoint), (int)(cam.pixelHeight / pixelsPerPoint))); } var selected = new Dictionary <ProBuilderMesh, HashSet <Face> >(); foreach (var pb in selectable) { if (!pb.selectable) { continue; } HashSet <Face> selectedFaces = new HashSet <Face>(); Transform trs = pb.transform; Vector3[] positions = pb.positionsInternal; Vector3[] screenPoints = new Vector3[pb.vertexCount]; for (int nn = 0; nn < pb.vertexCount; nn++) { screenPoints[nn] = cam.ScreenToGuiPoint(cam.WorldToScreenPoint(trs.TransformPoint(positions[nn])), pixelsPerPoint); } for (int n = 0; n < pb.facesInternal.Length; n++) { Face face = pb.facesInternal[n]; // rect select = complete if (options.rectSelectMode == RectSelectMode.Complete) { // face is behind the camera if (screenPoints[face.indexesInternal[0]].z < cam.nearClipPlane) { continue; } // only check the first index per quad, and if it checks out, then check every other point if (rect.Contains(screenPoints[face.indexesInternal[0]])) { bool nope = false; for (int q = 1; q < face.distinctIndexesInternal.Length; q++) { int index = face.distinctIndexesInternal[q]; if (screenPoints[index].z < cam.nearClipPlane || !rect.Contains(screenPoints[index])) { nope = true; break; } } if (!nope) { if (!options.depthTest || !HandleUtility.PointIsOccluded(cam, pb, trs.TransformPoint(Math.Average(positions, face.distinctIndexesInternal)))) { selectedFaces.Add(face); } } } } // rect select = partial else { Bounds2D poly = new Bounds2D(screenPoints, face.edgesInternal); bool overlaps = false; if (poly.Intersects(rect)) { // if rect contains one point of polygon, it overlaps for (int nn = 0; nn < face.distinctIndexesInternal.Length && !overlaps; nn++) { Vector3 p = screenPoints[face.distinctIndexesInternal[nn]]; overlaps = p.z > cam.nearClipPlane && rect.Contains(p); } // if polygon contains one point of rect, it overlaps. otherwise check for edge intersections if (!overlaps) { Vector2 tl = new Vector2(rect.xMin, rect.yMax); Vector2 tr = new Vector2(rect.xMax, rect.yMax); Vector2 bl = new Vector2(rect.xMin, rect.yMin); Vector2 br = new Vector2(rect.xMax, rect.yMin); overlaps = Math.PointInPolygon(screenPoints, poly, face.edgesInternal, tl); if (!overlaps) { overlaps = Math.PointInPolygon(screenPoints, poly, face.edgesInternal, tr); } if (!overlaps) { overlaps = Math.PointInPolygon(screenPoints, poly, face.edgesInternal, br); } if (!overlaps) { overlaps = Math.PointInPolygon(screenPoints, poly, face.edgesInternal, bl); } // if any polygon edge intersects rect for (int nn = 0; nn < face.edgesInternal.Length && !overlaps; nn++) { if (Math.GetLineSegmentIntersect(tr, tl, screenPoints[face.edgesInternal[nn].a], screenPoints[face.edgesInternal[nn].b])) { overlaps = true; } else if (Math.GetLineSegmentIntersect(tl, bl, screenPoints[face.edgesInternal[nn].a], screenPoints[face.edgesInternal[nn].b])) { overlaps = true; } else if (Math.GetLineSegmentIntersect(bl, br, screenPoints[face.edgesInternal[nn].a], screenPoints[face.edgesInternal[nn].b])) { overlaps = true; } else if (Math.GetLineSegmentIntersect(br, tl, screenPoints[face.edgesInternal[nn].a], screenPoints[face.edgesInternal[nn].b])) { overlaps = true; } } } } // don't test occlusion since that case is handled special if (overlaps) { selectedFaces.Add(face); } } } selected.Add(pb, selectedFaces); } return(selected); }