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;
            }
        }
Beispiel #3
0
        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);
        }