static void FitUVs(Vector2[] uvs, IList <int> indexes) { var bounds = new Bounds2D(); bounds.SetWithPoints(uvs, indexes); var c = bounds.center; var s = Mathf.Max(bounds.size.x, bounds.size.y); for (int i = 0; i < indexes.Count; i++) { var uv = uvs[indexes[i]]; uv.x = ((uv.x - c.x) / s) + c.x; uv.y = ((uv.y - c.y) / s) + c.y; uvs[indexes[i]] = uv; } }
static void StretchUVs(Vector2[] uvs, IList <int> indexes) { var bounds = new Bounds2D(); bounds.SetWithPoints(uvs, indexes); var c = bounds.center; var s = bounds.size; for (int i = 0; i < indexes.Count; i++) { var uv = uvs[indexes[i]]; uv.x = ((uv.x - c.x) / s.x) + c.x; uv.y = ((uv.y - c.y) / s.y) + c.y; uvs[indexes[i]] = uv; } }
static void ApplyUVSettings(Vector2[] uvs, int[] indexes, AutoUnwrapSettings uvSettings) { int len = indexes.Length; switch (uvSettings.fill) { case AutoUnwrapSettings.Fill.Tile: break; case AutoUnwrapSettings.Fill.Fit: FitUVs(uvs, indexes); break; case AutoUnwrapSettings.Fill.Stretch: StretchUVs(uvs, indexes); break; } if (!uvSettings.useWorldSpace && uvSettings.anchor != AutoUnwrapSettings.Anchor.None) { ApplyUVAnchor(uvs, indexes, uvSettings.anchor); } // Apply transform last, so that fill and justify don't override it. if (uvSettings.scale.x != 1f || uvSettings.scale.y != 1f || uvSettings.rotation != 0f) { Vector2 center = Bounds2D.Center(uvs, indexes); for (int i = 0; i < len; i++) { uvs[indexes[i]] = uvs[indexes[i]].ScaleAroundPoint(center, uvSettings.scale); uvs[indexes[i]] = uvs[indexes[i]].RotateAroundPoint(center, uvSettings.rotation); } } if (uvSettings.flipU || uvSettings.flipV || uvSettings.swapUV) { for (int i = 0; i < len; i++) { float u = uvs[indexes[i]].x, v = uvs[indexes[i]].y; if (uvSettings.flipU) { u = -u; } if (uvSettings.flipV) { v = -v; } if (!uvSettings.swapUV) { uvs[indexes[i]].x = u; uvs[indexes[i]].y = v; } else { uvs[indexes[i]].x = v; uvs[indexes[i]].y = u; } } } for (int i = 0; i < indexes.Length; i++) { uvs[indexes[i]].x -= uvSettings.offset.x; uvs[indexes[i]].y -= uvSettings.offset.y; } }
static void ApplyUVSettings(Vector2[] uvs, IList <int> indexes, AutoUnwrapSettings uvSettings) { int len = indexes.Count; Bounds2D bounds = new Bounds2D(uvs, indexes); switch (uvSettings.fill) { case AutoUnwrapSettings.Fill.Tile: break; case AutoUnwrapSettings.Fill.Fit: var max = Mathf.Max(bounds.size.x, bounds.size.y); ScaleUVs(uvs, indexes, new Vector2(max, max), bounds); bounds.center /= max; break; case AutoUnwrapSettings.Fill.Stretch: ScaleUVs(uvs, indexes, bounds.size, bounds); bounds.center /= bounds.size; break; } // Apply transform last, so that fill and justify don't override it. if (uvSettings.scale.x != 1f || uvSettings.scale.y != 1f || uvSettings.rotation != 0f) { // apply an offset to the positions relative to UV scale before rotation or scale is applied so that // UVs remain static in UV space Vector2 scaledCenter = bounds.center * uvSettings.scale; Vector2 delta = bounds.center - scaledCenter; Vector2 center = scaledCenter; for (int i = 0; i < len; i++) { uvs[indexes[i]] -= delta; uvs[indexes[i]] = uvs[indexes[i]].ScaleAroundPoint(center, uvSettings.scale); uvs[indexes[i]] = uvs[indexes[i]].RotateAroundPoint(center, uvSettings.rotation); } } if (!uvSettings.useWorldSpace && uvSettings.anchor != AutoUnwrapSettings.Anchor.None) { ApplyUVAnchor(uvs, indexes, uvSettings.anchor); } if (uvSettings.flipU || uvSettings.flipV || uvSettings.swapUV) { for (int i = 0; i < len; i++) { float u = uvs[indexes[i]].x, v = uvs[indexes[i]].y; if (uvSettings.flipU) { u = -u; } if (uvSettings.flipV) { v = -v; } if (!uvSettings.swapUV) { uvs[indexes[i]].x = u; uvs[indexes[i]].y = v; } else { uvs[indexes[i]].x = v; uvs[indexes[i]].y = u; } } } for (int i = 0; i < indexes.Count; i++) { uvs[indexes[i]].x -= uvSettings.offset.x; uvs[indexes[i]].y -= uvSettings.offset.y; } }
/// <summary> /// Picks the <see cref="Face" /> objects contained within a rect (rectangular selection). /// </summary> /// <param name="cam">Use this camera to evaluate whether any face object(s) are inside the `rect`.</param> /// <param name="rect">Rect is in GUI space, where 0,0 is the top left of the screen, and `width = cam.pixelWidth / pointsPerPixel`.</param> /// <param name="selectable">The collection of objects to check for selectability. ProBuilder verifies whether these objects fall inside the `rect`.</param> /// <param name="options">The options that define whether to include mesh elements that are hidden and whether to consider elements that only partially fall inside the `rect`.</param> /// <param name="pixelsPerPoint"> /// Scale the render texture to match `rect` coordinates. By default, ProBuilder doesn't scale the texture (the default value is set to 1.0), /// but you can specify <see cref="UnityEditor.EditorGUIUtility.pixelsPerPoint" /> if you need to use a different number of screen pixels per point.</param> /// <returns>A dictionary of ProBuilderMesh and Face objects that are in the selection `rect`. </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); }