/// <summary>
        /// Provided two faces, this method will attempt to project @f2 and align its size, rotation, and position to match
        /// the shared edge on f1.  Returns true on success, false otherwise.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="f1"></param>
        /// <param name="f2"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public static bool AutoStitch(ProBuilderMesh mesh, Face f1, Face f2, int channel)
        {
            var wings = WingedEdge.GetWingedEdges(mesh, new [] { f1, f2 });

            var sharedEdge = wings.FirstOrDefault(x => x.face == f1 && x.opposite != null && x.opposite.face == f2);

            if (sharedEdge == null)
            {
                return(false);
            }

            if (f1.manualUV)
            {
                f2.manualUV = true;
            }

            f1.textureGroup = -1;
            f2.textureGroup = -1;

            Projection.PlanarProject(mesh, f2);

            if (AlignEdges(mesh, f2, sharedEdge.edge.local, sharedEdge.opposite.edge.local, channel))
            {
                if (!f2.manualUV)
                {
                    UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(mesh, new [] { f2 });
                }

                return(true);
            }

            return(false);
        }
    public void SetManualFaceToAuto_MatchesOriginalUVs(
        [ValueSource("AutoUVOffsetParameters")] Vector2 offset,
        [ValueSource("AutoUVRotationParameters")] float rotation,
        [ValueSource("AutoUVScaleParameters")] Vector2 scale)
    {
        var unwrap = face.uv;

        unwrap.offset   = offset;
        unwrap.rotation = rotation;
        unwrap.scale    = scale;

        face.uv = unwrap;

        // Verify that UV settings have actually been applied
        Assume.That(face.uv.offset, Is.EqualTo(offset));
        Assume.That(face.uv.rotation, Is.EqualTo(rotation));
        Assume.That(face.uv.scale, Is.EqualTo(scale));
        Assume.That(face.manualUV, Is.EqualTo(false));

        mesh.Refresh(RefreshMask.UV);

        // Verify that the UVs are in the correct place
        Assume.That(GetEdgeRotation(mesh, verticalEdge), Is.EqualTo(rotation).Within(.1f));
        Assume.That(GetEdgeScale(mesh, verticalEdge), Is.EqualTo(scale.y).Within(.1f));
        Assume.That(GetEdgeScale(mesh, horizontalEdge), Is.EqualTo(scale.x).Within(.1f));
        // Offset is flipped in code for legacy reasons
        var center = Bounds2D.Center(mesh.texturesInternal, face.distinctIndexesInternal);

        Assume.That(center.x, Is.EqualTo(-offset.x).Within(.1f));
        Assume.That(center.y, Is.EqualTo(-offset.y).Within(.1f));

        face.uv       = AutoUnwrapSettings.defaultAutoUnwrapSettings;
        face.manualUV = true;

        // Verify that UV settings have been reset
        Assume.That(face.uv.offset, Is.EqualTo(new Vector2(0f, 0f)));
        Assume.That(face.uv.rotation, Is.EqualTo(0f));
        Assume.That(face.uv.scale, Is.EqualTo(new Vector2(1f, 1f)));
        Assume.That(face.manualUV, Is.EqualTo(true));

        // This sets the manualFlag to false, sets the AutoUnwrap settings, and rebuilds UVs
        UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(mesh, new [] { face });

        Assert.That(face.uv.offset.x, Is.EqualTo(offset.x).Within(.1f));
        Assert.That(face.uv.offset.y, Is.EqualTo(offset.y).Within(.1f));
        Assert.That(face.uv.rotation, Is.EqualTo(rotation).Within(.1f));
        Assert.That(face.uv.scale.x, Is.EqualTo(scale.x).Within(.1f));
        Assert.That(face.uv.scale.y, Is.EqualTo(scale.y).Within(.1f));
        Assert.That(face.manualUV, Is.EqualTo(false));
    }
        static List <ProBuilderMesh> CombineToNewMeshes(IEnumerable <ProBuilderMesh> meshes)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }

            if (!meshes.Any() || meshes.Count() < 2)
            {
                return(null);
            }

            var vertices       = new List <Vertex>();
            var faces          = new List <Face>();
            var autoUvFaces    = new List <Face>();
            var sharedVertices = new List <SharedVertex>();
            var sharedTextures = new List <SharedVertex>();
            int offset         = 0;
            var materialMap    = new List <Material>();

            AccumulateMeshesInfo(
                meshes,
                offset,
                ref vertices,
                ref faces,
                ref autoUvFaces,
                ref sharedVertices,
                ref sharedTextures,
                ref materialMap
                );

            var res   = SplitByMaxVertexCount(vertices, faces, sharedVertices, sharedTextures);
            var pivot = meshes.LastOrDefault().transform.position;

            foreach (var m in res)
            {
                m.renderer.sharedMaterials = materialMap.ToArray();
                InternalMeshUtility.FilterUnusedSubmeshIndexes(m);
                m.SetPivot(pivot);
                UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(m, autoUvFaces);
            }

            return(res);
        }
        protected override void OnToolDisengaged()
        {
            var isFaceMode = ProBuilderEditor.selectMode.ContainsFlag(SelectMode.TextureFace | SelectMode.Face);

            foreach (var mesh in elementSelection)
            {
                if (!(mesh is MeshAndTextures))
                {
                    continue;
                }

                var textures = ((MeshAndTextures)mesh).textures;
                mesh.mesh.SetUVs(k_TextureChannel, textures);

                if (isFaceMode)
                {
                    UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(mesh.mesh, mesh.mesh.selectedFacesInternal.Where(x => !x.manualUV));
                }
                else
                {
                    var indices = new HashSet <int>(mesh.elementGroups.SelectMany(x => x.indices));

                    foreach (var face in mesh.mesh.facesInternal)
                    {
                        foreach (var index in face.distinctIndexesInternal)
                        {
                            if (indices.Contains(index))
                            {
                                face.manualUV = true;
                                break;
                            }
                        }
                    }
                }
            }
        }
Example #5
0
        public override Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation)
        {
            var meshSize = Math.Abs(size);

            m_Radius = System.Math.Min(meshSize.x, meshSize.z);
            var height = meshSize.y;

            var subdivAxis = m_NumberOfSides;

            // template is outer ring - radius refers to outer ring always
            Vector3[] template = new Vector3[subdivAxis];

            for (int i = 0; i < subdivAxis; i++)
            {
                Vector2 ct = Math.PointInCircumference(m_Radius, i * (360f / subdivAxis), Vector2.zero);
                template[i] = new Vector3(ct.x, -height / 2f, ct.y);
            }

            List <Vector3> v = new List <Vector3>();
            List <Face>    f = new List <Face>();

            // build sides
            for (int i = 0; i < subdivAxis; i++)
            {
                // side face
                v.Add(template[i]);
                v.Add((i < subdivAxis - 1) ? template[i + 1] : template[0]);
                v.Add(Vector3.up * height / 2f);

                // bottom face
                v.Add(template[i]);
                v.Add((i < subdivAxis - 1) ? template[i + 1] : template[0]);
                v.Add(Vector3.down * height / 2f);
            }

            List <Face> sideFaces = new List <Face>();

            for (int i = 0; i < subdivAxis * 6; i += 6)
            {
                Face face = new Face(new int[3] {
                    i + 2, i + 1, i + 0
                });
                f.Add(face);
                sideFaces.Add(face);
                f.Add(new Face(new int[3] {
                    i + 3, i + 4, i + 5
                }));
            }

            var sizeSigns = Math.Sign(size);

            for (int i = 0; i < v.Count; i++)
            {
                v[i] = Vector3.Scale(rotation * v[i], sizeSigns);
            }

            var sizeSign = Mathf.Sign(size.x) * Mathf.Sign(size.y) * Mathf.Sign(size.z);

            if (sizeSign < 0)
            {
                foreach (var face in f)
                {
                    face.Reverse();
                }
            }

            mesh.RebuildWithPositionsAndFaces(v, f);

            mesh.unwrapParameters = new UnwrapParameters()
            {
                packMargin = 30f
            };

            // Set the UVs manually for the side faces, so that they are uniform.
            // Calculate the UVs for the first face, then set the others to the same.
            var firstFace = sideFaces[0];
            var uv        = firstFace.uv;

            uv.anchor          = AutoUnwrapSettings.Anchor.LowerLeft;
            firstFace.uv       = uv;
            firstFace.manualUV = true;
            // Always use up vector for projection of side faces.
            // Otherwise the lines in the PB texture end up crooked.
            UvUnwrapping.Unwrap(mesh, firstFace, projection: Vector3.up);
            for (int i = 1; i < sideFaces.Count; i++)
            {
                var sideFace = sideFaces[i];
                sideFace.manualUV = true;
                UvUnwrapping.CopyUVs(mesh, firstFace, sideFace);
            }
            mesh.RefreshUV(sideFaces);

            return(UpdateBounds(mesh, size, rotation, new Bounds()));
        }
        /// <summary>
        /// Merge a collection of <see cref="ProBuilderMesh"/> objects to as few meshes as possible. It will re-use the meshTarget object as the first
        /// destination for the first <see cref="ProBuilderMesh.maxVertexCount"/> -1 vertices. If the sum of vertices is above <see cref="ProBuilderMesh.maxVertexCount"/> - 1
        /// it will generate new meshes unless there is a single mesh left in which case it will append it to the return list.
        /// </summary>
        /// <param name="meshes">A collection of meshes to be merged. Note: it is expected that meshes includes meshTarget.</param>
        /// <param name="meshTarget">A mesh which will be used as the starting point for merging and which will be kept as reference/target.</param>
        /// <returns>
        /// A list of merged meshes. In most cases this will be a single mesh corresponding to meshTarget. However it can be multiple in cases
        /// where the resulting vertex count exceeds the maximum allowable value.
        /// </returns>
        public static List <ProBuilderMesh> Combine(IEnumerable <ProBuilderMesh> meshes, ProBuilderMesh meshTarget)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }

            if (meshTarget == null)
            {
                throw new ArgumentNullException("meshTarget");
            }

            if (!meshes.Any() || meshes.Count() < 2)
            {
                return(null);
            }

            if (!meshes.Contains(meshTarget))
            {
                return(null);
            }

            var vertices        = new List <Vertex>(meshTarget.GetVertices());
            var faces           = new List <Face>(meshTarget.facesInternal);
            var sharedVertices  = new List <SharedVertex>(meshTarget.sharedVertices);
            var sharedTextures  = new List <SharedVertex>(meshTarget.sharedTextures);
            int offset          = meshTarget.vertexCount;
            var materialMap     = new List <Material>(meshTarget.renderer.sharedMaterials);
            var targetTransform = meshTarget.transform;

            var firstMeshContributors     = new List <ProBuilderMesh>();
            var remainderMeshContributors = new List <ProBuilderMesh>();

            var currentMeshVertexCount = offset;

            foreach (var mesh in meshes)
            {
                if (mesh != meshTarget)
                {
                    if (currentMeshVertexCount + mesh.vertexCount < ProBuilderMesh.maxVertexCount)
                    {
                        currentMeshVertexCount += mesh.vertexCount;
                        firstMeshContributors.Add(mesh);
                    }
                    else
                    {
                        remainderMeshContributors.Add(mesh);
                    }
                }
            }

            var autoUvFaces = new List <Face>();

            AccumulateMeshesInfo(
                firstMeshContributors,
                offset,
                ref vertices,
                ref faces,
                ref autoUvFaces,
                ref sharedVertices,
                ref sharedTextures,
                ref materialMap,
                targetTransform
                );

            meshTarget.SetVertices(vertices);
            meshTarget.faces          = faces;
            meshTarget.sharedVertices = sharedVertices;
            meshTarget.sharedTextures = sharedTextures != null?sharedTextures.ToArray() : null;

            meshTarget.renderer.sharedMaterials = materialMap.ToArray();
            meshTarget.ToMesh();
            meshTarget.Refresh();
            UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(meshTarget, autoUvFaces);

            MeshValidation.EnsureMeshIsValid(meshTarget, out int removedVertices);

            var returnedMesh = new List <ProBuilderMesh>()
            {
                meshTarget
            };

            if (remainderMeshContributors.Count > 1)
            {
                var newMeshes = CombineToNewMeshes(remainderMeshContributors);
                foreach (var mesh in newMeshes)
                {
                    MeshValidation.EnsureMeshIsValid(mesh, out removedVertices);
                    returnedMesh.Add(mesh);
                }
            }
            else if (remainderMeshContributors.Count == 1)
            {
                returnedMesh.Add(remainderMeshContributors[0]);
            }

            return(returnedMesh);
        }
            public TranslateTextureSelection(ProBuilderMesh mesh, PivotPoint pivot)
                : base(mesh, pivot)
            {
                var faces = mesh.faces;

                m_FaceAndScale = mesh.selectedFaceIndexes.Select(x =>
                                                                 new SimpleTuple <Face, Vector2>(faces[x], UvUnwrapping.GetUVTransform(mesh, faces[x]).scale))
                                 .ToArray();
            }