internal override void OnBrushEnter(EditableObject target, BrushSettings settings)
        {
            base.OnBrushEnter(target, settings);

            if (!m_LikelyToSupportVertexSculpt)
            {
                return;
            }

            EditableObjectData data;

            if (!m_EditableObjectsData.TryGetValue(target, out data))
            {
                data = new EditableObjectData();
                m_EditableObjectsData.Add(target, data);
            }
            data.vertices          = target.editMesh.vertices;
            data.neighborLookup    = PolyMeshUtility.GetAdjacentVertices(target.editMesh);
            data.commonVertices    = PolyMeshUtility.GetCommonVertices(target.editMesh);
            data.commonVertexCount = data.commonVertices.Length;
        }
Пример #2
0
        static HierarchyChanged()
        {
            EditorApplication.hierarchyChanged += () =>
            {
                foreach (var gameObject in Selection.gameObjects)
                {
                    var mesh = Util.GetMesh(gameObject);
                    var id   = EditableObject.GetMeshId(mesh);

                    // if the mesh is an instance managed by polybrush check that it's not a duplicate.
                    if (id != -1)
                    {
                        if (id != gameObject.GetInstanceID() && EditorUtility.InstanceIDToObject(id) != null)
                        {
                            mesh      = PolyMeshUtility.DeepCopy(mesh);
                            mesh.name = EditableObject.k_MeshInstancePrefix + gameObject.GetInstanceID();

                            var mf       = gameObject.GetComponent <MeshFilter>();
                            var sf       = gameObject.GetComponent <SkinnedMeshRenderer>();
                            var polyMesh = gameObject.GetComponent <PolybrushMesh>();

                            if (polyMesh != null)
                            {
                                polyMesh.SetMesh(mesh);
                                PrefabUtility.RecordPrefabInstancePropertyModifications(polyMesh);
                            }
                            else if (mf != null)
                            {
                                mf.sharedMesh = mesh;
                            }
                            else if (sf != null)
                            {
                                sf.sharedMesh = mesh;
                            }
                        }
                    }
                }
            };
        }
        void RebuildCaches(EditableObjectData data)
        {
            PolyMesh mesh = data.CacheTarget.editMesh;

            data.VertexCount    = mesh.vertexCount;
            data.TriangleLookup = PolyMeshUtility.GetAdjacentTriangles(mesh);

            if (meshAttributes == null)
            {
                // clear caches
                data.SplatCache   = null;
                data.SplatCurrent = null;
                data.SplatTarget  = null;
                data.SplatErase   = null;
                return;
            }

            data.SplatCache = new SplatSet(mesh, meshAttributes);
            SetupBaseTextures(data.SplatCache);
            data.SplatCurrent = new SplatSet(data.SplatCache);
            data.SplatTarget  = new SplatSet(data.VertexCount, meshAttributes);
            data.SplatErase   = new SplatSet(data.VertexCount, meshAttributes);
        }
Пример #4
0
        // Called when the mouse begins hovering an editable object.
        internal override void OnBrushEnter(EditableObject target, BrushSettings settings)
        {
            base.OnBrushEnter(target, settings);

            if (target.graphicsMesh == null)
            {
                return;
            }

            RebuildCaches(target, settings);

            m_TriangleLookup = PolyMeshUtility.GetAdjacentTriangles(target.editMesh);

            MeshRenderer mr = target.gameObjectAttached.GetComponent <MeshRenderer>();

            if (mr != null && mr.sharedMaterials != null)
            {
                m_LikelySupportsVertexColors = mr.sharedMaterials.Any(x => x != null && x.shader != null && PolyShaderUtil.SupportsVertexColors(x.shader));
            }
            else
            {
                m_LikelySupportsVertexColors = false;
            }
        }
Пример #5
0
        /// <summary>
        /// Applies mesh changes back to the pb_Object (if necessary).  Optionally does a
        /// mesh rebuild.
        /// </summary>
        /// <param name="rebuildMesh">Only applies to ProBuilder meshes.</param>
        /// <param name="optimize">Determines if the mesh collisions are rebuilt (if that option is enabled) or if
        /// the mehs is a probuilder object, the mesh is optimized (condensed to share verts, other
        /// otpimziations etc) </param>
        internal void Apply(bool rebuildMesh, bool optimize = false)
        {
            if (m_PolybrushMesh.mode == PolybrushMesh.Mode.AdditionalVertexStream)
            {
                if (PolyEditor.instance.tool == BrushTool.RaiseLower ||
                    PolyEditor.instance.tool == BrushTool.Smooth)
                {
                    if (s_RebuildNormals.value && (modifiedChannels & MeshChannel.Position) > 0)
                    {
                        PolyMeshUtility.RecalculateNormals(editMesh);
                    }

                    if (optimize)
                    {
                        graphicsMesh.RecalculateBounds();
                        UpdateMeshCollider();
                    }
                }

                editMesh.ApplyAttributesToUnityMesh(graphicsMesh, modifiedChannels);
                graphicsMesh.UploadMeshData(false);
                EditorUtility.SetDirty(gameObjectAttached.GetComponent <Renderer>());

                if (m_PolybrushMesh.componentsCache.MeshFilter)
                {
                    Undo.RecordObject(m_PolybrushMesh.componentsCache.MeshFilter, "Assign Polymesh to MeshFilter");
                }

                if (m_PolybrushMesh)
                {
                    m_PolybrushMesh.SynchronizeWithMeshRenderer();
                }
            }


#if PROBUILDER_4_0_OR_NEWER
            // if it's a probuilder object rebuild the mesh without optimization
            if (isProBuilderObject)
            {
                ProBuilderBridge.SetPositions(gameObjectAttached, editMesh.vertices);
                ProBuilderBridge.SetTangents(gameObjectAttached, editMesh.tangents);

                if (editMesh.colors != null && editMesh.colors.Length == editMesh.vertexCount)
                {
                    Color[] colors = System.Array.ConvertAll(editMesh.colors, x => (Color)x);
                    ProBuilderBridge.SetColors(gameObjectAttached, colors);
                }

                if (rebuildMesh)
                {
                    ProBuilderBridge.ToMesh(gameObjectAttached);
                    ProBuilderBridge.Refresh(gameObjectAttached,
                                             optimize
                            ? ProBuilderBridge.RefreshMask.All
                            : (ProBuilderBridge.RefreshMask.Colors
                               | ProBuilderBridge.RefreshMask.Normals
                               | ProBuilderBridge.RefreshMask.Tangents));
                }
            }
#endif

            if (m_PolybrushMesh.mode == PolybrushMesh.Mode.AdditionalVertexStream)
            {
                modifiedChannels = MeshChannel.Null;
                return;
            }

            if (PolyEditor.instance.tool == BrushTool.RaiseLower ||
                PolyEditor.instance.tool == BrushTool.Smooth)
            {
                if (s_RebuildNormals.value)// && (modifiedChannels & MeshChannel.Position) > 0)
                {
                    PolyMeshUtility.RecalculateNormals(editMesh);
                }

                if (optimize)
                {
                    UpdateMeshCollider();
                    graphicsMesh.RecalculateBounds();
                }
            }

            editMesh.ApplyAttributesToUnityMesh(graphicsMesh, modifiedChannels);

            if (m_PolybrushMesh.componentsCache.MeshFilter)
            {
                Undo.RecordObject(m_PolybrushMesh.componentsCache.MeshFilter, "Assign Polymesh to MeshFilter");
            }

            m_PolybrushMesh.SynchronizeWithMeshRenderer();

            modifiedChannels = MeshChannel.Null;
        }
Пример #6
0
        private void Initialize(GameObject go)
        {
            CheckBackwardCompatiblity(go);

            gameObjectAttached = go;
            isProBuilderObject = false;

#if PROBUILDER_4_0_OR_NEWER
            if (ProBuilderBridge.ProBuilderExists())
            {
                isProBuilderObject = ProBuilderBridge.IsValidProBuilderMesh(gameObjectAttached);
            }
#endif
            Mesh         mesh         = null;
            MeshRenderer meshRenderer = gameObjectAttached.GetComponent <MeshRenderer>();
            meshFilter        = gameObjectAttached.GetComponent <MeshFilter>();
            _skinMeshRenderer = gameObjectAttached.GetComponent <SkinnedMeshRenderer>();

            originalMesh = go.GetMesh();

            if (originalMesh == null && _skinMeshRenderer != null)
            {
                originalMesh = _skinMeshRenderer.sharedMesh;
            }

            m_PolybrushMesh = gameObjectAttached.GetComponent <PolybrushMesh>();

            if (m_PolybrushMesh == null)
            {
                m_PolybrushMesh = Undo.AddComponent <PolybrushMesh>(gameObjectAttached);
                m_PolybrushMesh.Initialize();
                m_PolybrushMesh.mode = (s_UseAdditionalVertexStreams) ? PolybrushMesh.Mode.AdditionalVertexStream : PolybrushMesh.Mode.Mesh;
            }

            //attach the skinmesh ref to the polybrushmesh
            //it will be used when making a prefab containing a skin mesh. The limitation here is that the skin mesh must comes from an asset (which is 99.9999% of the time)
            if (_skinMeshRenderer != null)
            {
                Mesh sharedMesh = _skinMeshRenderer.sharedMesh;
                if (AssetDatabase.Contains(sharedMesh))
                {
                    m_PolybrushMesh.skinMeshRef = sharedMesh;
                }
            }

#if PROBUILDER_4_0_OR_NEWER
            // if it's a probuilder object rebuild the mesh without optimization
            if (isProBuilderObject)
            {
                if (ProBuilderBridge.IsValidProBuilderMesh(gameObjectAttached))
                {
                    ProBuilderBridge.ToMesh(gameObjectAttached);
                    ProBuilderBridge.Refresh(gameObjectAttached);
                }
            }
#endif

            if (meshRenderer != null || _skinMeshRenderer != null)
            {
                mesh = m_PolybrushMesh.storedMesh;

                if (mesh == null)
                {
                    mesh             = PolyMeshUtility.DeepCopy(originalMesh);
                    hadVertexStreams = false;
                }
                else
                {
                    //prevents leak
                    if (!MeshInstanceMatchesGameObject(mesh, gameObjectAttached))
                    {
                        mesh = PolyMeshUtility.DeepCopy(mesh);
                    }
                }

                mesh.name = k_MeshInstancePrefix + gameObjectAttached.GetInstanceID();
            }

            polybrushMesh.SetMesh(mesh);
            PrefabUtility.RecordPrefabInstancePropertyModifications(polybrushMesh);
            _graphicsMesh = m_PolybrushMesh.storedMesh;

            source = polybrushMesh.mode == PolybrushMesh.Mode.AdditionalVertexStream? ModelSource.AdditionalVertexStreams : PolyEditorUtility.GetMeshGUID(originalMesh);

            GenerateCompositeMesh();
        }
Пример #7
0
        /// <summary>
        /// Save any modifications to the EditableObject.  If the mesh is a scene mesh or imported mesh, it
        /// will be saved to a new asset.  If the mesh was originally an asset mesh, the asset is overwritten.
        /// </summary>
        /// <param name="mesh">mesh to save</param>
        /// <param name="meshFilter">will update the mesh filter with the new mesh if not null</param>
        /// <param name="skinnedMeshRenderer">will update the skinned mesh renderer with the new mesh if not null</param>
        /// <returns>return true if save was successful, false if user-canceled or otherwise failed.</returns>
        internal static bool SaveMeshAsset(Mesh mesh, MeshFilter meshFilter = null, SkinnedMeshRenderer skinnedMeshRenderer = null, int overridenDialogResult = -1, string overridenPath = "")
        {
            if (mesh == null)
            {
                return(false);
            }

            string save_path = !string.IsNullOrEmpty(overridenPath) ? overridenPath : DO_NOT_SAVE;

            ModelSource source = GetMeshGUID(mesh);

            switch (source)
            {
            case ModelSource.Asset:

                int saveChanges = overridenDialogResult != -1
                        ? overridenDialogResult
                        : EditorUtility.DisplayDialogComplex(
                    "Save Changes",
                    "Save changes to edited mesh?",
                    "Save",         // DIALOG_OK
                    "Cancel",       // DIALOG_CANCEL
                    "Save As");     // DIALOG_ALT

                if (saveChanges == DIALOG_OK)
                {
                    save_path = AssetDatabase.GetAssetPath(mesh);
                }
                else if (saveChanges == DIALOG_ALT)
                {
                    save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to");
                }
                else
                {
                    return(false);
                }

                break;

            case ModelSource.Imported:
            case ModelSource.Scene:
            default:
                save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to");
                break;
            }

            if (!save_path.Equals(DO_NOT_SAVE) && !string.IsNullOrEmpty(save_path))
            {
                Mesh existing  = AssetDatabase.LoadAssetAtPath <Mesh>(save_path);
                bool overwrite = existing != null;
                Mesh dst       = overwrite ? existing : new Mesh();
                PolyMeshUtility.Copy(mesh, dst);
                if (!overwrite)
                {
                    AssetDatabase.CreateAsset(dst, save_path);
                }
                AssetDatabase.Refresh();
                return(true);
            }

            // Save was canceled
            return(false);
        }
Пример #8
0
        /// <summary>
        /// Save any modifications to the EditableObject.  If the mesh is a scene mesh or imported mesh, it
        /// will be saved to a new asset.  If the mesh was originally an asset mesh, the asset is overwritten.
        /// </summary>
        /// <param name="mesh">mesh to save</param>
        /// <param name="meshFilter">will update the mesh filter with the new mesh if not null</param>
        /// <param name="skinnedMeshRenderer">will update the skinned mesh renderer with the new mesh if not null</param>
        /// <returns>return true if save was successful, false if user-canceled or otherwise failed.</returns>
        internal static bool SaveMeshAsset(Mesh mesh, MeshFilter meshFilter = null, SkinnedMeshRenderer skinnedMeshRenderer = null, int overridenDialogResult = -1, string overridenPath = "")
        {
            if (mesh == null)
            {
                return(false);
            }

            string save_path = !string.IsNullOrEmpty(overridenPath) ? overridenPath : DO_NOT_SAVE;

            ModelSource source = GetMeshGUID(mesh);

            switch (source)
            {
            case ModelSource.Asset:

                int saveChanges = overridenDialogResult != -1 ? overridenDialogResult :
                                  EditorUtility.DisplayDialogComplex(
                    "Save Changes",
                    "Save changes to edited mesh?",
                    "Save",                                                     // DIALOG_OK
                    "Cancel",                                                   // DIALOG_CANCEL
                    "Save As");                                                 // DIALOG_ALT

                if (saveChanges == DIALOG_OK)
                {
                    save_path = AssetDatabase.GetAssetPath(mesh);
                }
                else if (saveChanges == DIALOG_ALT)
                {
                    save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to");
                }
                else
                {
                    return(false);
                }

                break;

            case ModelSource.Imported:
            case ModelSource.Scene:
            default:
                save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to");
                break;
            }

            if (!save_path.Equals(DO_NOT_SAVE) && !string.IsNullOrEmpty(save_path))
            {
                Object existing = AssetDatabase.LoadMainAssetAtPath(save_path);

                if (existing != null && existing is Mesh)
                {
                    //if the mesh that we want to create is the same than the mesh found at this path, do nothing
                    if (existing.GetInstanceID() == mesh.GetInstanceID())
                    {
                        //nothing to do here
                    }
                    // save over an existing mesh asset
                    else
                    {
                        PolyMeshUtility.Copy((Mesh)existing, mesh);
                        Object.DestroyImmediate(mesh, true);
                    }
                }
                else
                {
                    Mesh newMesh = new Mesh();
                    PolyMeshUtility.Copy(newMesh, mesh);
                    AssetDatabase.CreateAsset(newMesh, save_path);
                }

                AssetDatabase.Refresh();

                return(true);
            }

            // Save was canceled
            return(false);
        }
        /// <summary>
        /// Calculates the per-vertex weight for each raycast hit and fills in brush target weights.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="settings"></param>
        /// <param name="tool"></param>
        /// <param name="bMode"></param>
        internal static void CalculateWeightedVertices(BrushTarget target, BrushSettings settings, BrushTool tool = BrushTool.None, BrushMode bMode = null)
        {
            if (target == null || settings == null)
            {
                return;
            }

            if (target.editableObject == null)
            {
                return;
            }

            bool  uniformScale = Math.VectorIsUniform(target.transform.lossyScale);
            float scale        = uniformScale ? 1f / target.transform.lossyScale.x : 1f;

            PolyMesh mesh = target.editableObject.visualMesh;

            Transform transform   = target.transform;
            int       vertexCount = mesh.vertexCount;

            Vector3[] vertices = mesh.vertices;

            if (!uniformScale)
            {
                // As we only increase size only when it's needed, always make sure to
                // use the vertexCount variable in loop statements and not the buffer length.
                if (s_WorldBuffer.Length < vertexCount)
                {
                    System.Array.Resize <Vector3>(ref s_WorldBuffer, vertexCount);
                }

                for (int i = 0; i < vertexCount; i++)
                {
                    s_WorldBuffer[i] = transform.TransformPoint(vertices[i]);
                }
                vertices = s_WorldBuffer;
            }

            AnimationCurve curve = settings.falloffCurve;
            float          radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f);

            Vector3        hitPosition = Vector3.zero;
            PolyRaycastHit hit;

            if (tool == BrushTool.Texture && mesh.subMeshCount > 1)
            {
                var   mode           = bMode as BrushModeTexture;
                int[] submeshIndices = mesh.subMeshes[mode.m_CurrentMeshACIndex].indexes;

                for (int n = 0; n < target.raycastHits.Count; n++)
                {
                    hit = target.raycastHits[n];
                    hit.SetVertexCount(vertexCount);

                    for (int i = 0; i < vertexCount; i++)
                    {
                        hit.weights[i] = 0f;
                    }

                    hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position);

                    for (int i = 0; i < submeshIndices.Length; i++)
                    {
                        int   currentIndex = submeshIndices[i];
                        float dist         = (hitPosition - vertices[currentIndex]).magnitude;
                        float delta        = radius - dist;

                        if (delta >= 0)
                        {
                            float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp(delta / falloff_mag, 0f, 1f)), 0f, 1f);

                            hit.weights[currentIndex] = weight;
                        }
                    }
                }
            }
            else
            {
                int[][] common = PolyMeshUtility.GetCommonVertices(mesh);

                Vector3 buf = Vector3.zero;

                for (int n = 0; n < target.raycastHits.Count; n++)
                {
                    hit = target.raycastHits[n];
                    hit.SetVertexCount(vertexCount);

                    hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position);

                    for (int i = 0; i < common.Length; i++)
                    {
                        int[] commonItem       = common[i];
                        int   commonArrayCount = commonItem.Length;

                        Math.Subtract(vertices[commonItem[0]], hitPosition, ref buf);

                        float sqrDist = buf.sqrMagnitude;

                        if (sqrDist > radius * radius)
                        {
                            for (int j = 0; j < commonArrayCount; j++)
                            {
                                hit.weights[commonItem[j]] = 0f;
                            }
                        }
                        else
                        {
                            float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp((radius - Mathf.Sqrt(sqrDist)) / falloff_mag, 0f, 1f)), 0f, 1f);

                            for (int j = 0; j < commonArrayCount; j++)
                            {
                                hit.weights[commonItem[j]] = weight;
                            }
                        }
                    }
                }
            }

            target.GetAllWeights(true);
        }
Пример #10
0
        /// <summary>
        /// Calculates the per-vertex weight for each raycast hit and fills in brush target weights.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="settings"></param>
        /// <param name="tool"></param>
        /// <param name="bMode"></param>
        internal static void CalculateWeightedVertices(BrushTarget target, BrushSettings settings, BrushTool tool = BrushTool.None, BrushMode bMode = null)
        {
            //null checks
            if (target == null || settings == null)
            {
                return;
            }

            if (target.editableObject == null)
            {
                return;
            }

            bool  uniformScale = PolyMath.VectorIsUniform(target.transform.lossyScale);
            float scale        = uniformScale ? 1f / target.transform.lossyScale.x : 1f;

            PolyMesh mesh = target.editableObject.visualMesh;

            if (tool == BrushTool.Texture && mesh.subMeshCount > 1)
            {
                var   mode           = bMode as BrushModeTexture;
                int[] submeshIndices = mesh.subMeshes[mode.currentMeshACIndex].indexes;

                //List<List<int>> common = PolyMeshUtility.GetCommonVertices(mesh);

                Transform transform   = target.transform;
                int       vertexCount = mesh.vertexCount;
                Vector3[] vertices    = mesh.vertices;

                if (!uniformScale)
                {
                    Vector3[] world = new Vector3[vertexCount];
                    for (int i = 0; i < vertexCount; i++)
                    {
                        world[i] = transform.TransformPoint(vertices[i]);
                    }
                    vertices = world;
                }

                AnimationCurve curve = settings.falloffCurve;
                float          radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f);

                Vector3        hitPosition = Vector3.zero;
                PolyRaycastHit hit;

                for (int n = 0; n < target.raycastHits.Count; n++)
                {
                    hit = target.raycastHits[n];
                    hit.SetVertexCount(vertexCount);

                    for (int i = 0; i < vertexCount; i++)
                    {
                        hit.weights[i] = 0f;
                    }

                    hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position);

                    for (int i = 0; i < submeshIndices.Length; i++)
                    {
                        int   currentIndex = submeshIndices[i];
                        float dist         = (hitPosition - vertices[currentIndex]).magnitude;
                        float delta        = radius - dist;

                        if (delta >= 0)
                        {
                            float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp(delta / falloff_mag, 0f, 1f)), 0f, 1f);

                            hit.weights[currentIndex] = weight;
                        }
                    }
                }
            }
            else
            {
                List <List <int> > common = PolyMeshUtility.GetCommonVertices(mesh);

                Transform transform   = target.transform;
                int       vertexCount = mesh.vertexCount;
                Vector3[] vertices    = mesh.vertices;

                if (!uniformScale)
                {
                    Vector3[] world = new Vector3[vertexCount];
                    for (int i = 0; i < vertexCount; i++)
                    {
                        world[i] = transform.TransformPoint(vertices[i]);
                    }
                    vertices = world;
                }

                AnimationCurve curve = settings.falloffCurve;
                float          radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f);

                Vector3        hitPosition = Vector3.zero;
                PolyRaycastHit hit;

                for (int n = 0; n < target.raycastHits.Count; n++)
                {
                    hit = target.raycastHits[n];
                    hit.SetVertexCount(vertexCount);

                    hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position);

                    for (int i = 0; i < common.Count; i++)
                    {
                        int   commonArrayCount = common[i].Count;
                        float sqrDist          = (hitPosition - vertices[common[i][0]]).sqrMagnitude;

                        if (sqrDist > radius * radius)
                        {
                            for (int j = 0; j < commonArrayCount; j++)
                            {
                                hit.weights[common[i][j]] = 0f;
                            }
                        }
                        else
                        {
                            float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp((radius - Mathf.Sqrt(sqrDist)) / falloff_mag, 0f, 1f)), 0f, 1f);

                            for (int j = 0; j < commonArrayCount; j++)
                            {
                                hit.weights[common[i][j]] = weight;
                            }
                        }
                    }
                }
            }
            target.GetAllWeights(true);
        }