void OnDisable() { Selection.selectionChanged -= OnSelectionChanged; #if UNITY_2019_1_OR_NEWER SceneView.duringSceneGui -= OnSceneGUI; #else SceneView.onSceneGUIDelegate -= OnSceneGUI; #endif Undo.undoRedoPerformed -= UndoRedoPerformed; //EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyWindowItemChanged; #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { ProBuilderBridge.UnsubscribeToSelectModeChanged(OnProBuilderSelectModeChanged); } #endif // store local changes to brushSettings if (brushSettings != null) { var js = JsonUtility.ToJson(brushSettings, true); EditorPrefs.SetString(k_BrushSettingsPref, js); } // don't iterate here! FinalizeAndReset does that OnBrushExit(m_LastHoveredGameObject); FinalizeAndResetHovering(); PreviewsDatabase.UnloadCache(); }
internal override void RegisterUndo(BrushTarget brushTarget) { #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.IsValidProBuilderMesh(brushTarget.gameObject)) { UnityEngine.Object pbMesh = ProBuilderBridge.GetProBuilderComponent(brushTarget.gameObject); if (pbMesh != null) { Undo.RegisterCompleteObjectUndo(pbMesh, UndoMessage); modifiedPbMeshes.Add(brushTarget.gameObject); } else { Undo.RegisterCompleteObjectUndo(brushTarget.editableObject.polybrushMesh, UndoMessage); modifiedMeshes.Add(brushTarget.editableObject.polybrushMesh.polyMesh); } } else #endif { Undo.RegisterCompleteObjectUndo(brushTarget.editableObject.polybrushMesh, UndoMessage); modifiedMeshes.Add(brushTarget.editableObject.polybrushMesh.polyMesh); } brushTarget.editableObject.isDirty = true; }
/// <summary> /// Update the current state of the mesh preview. /// As AssetPreview API is asynchronous, we need to keep requesting the asset preview /// until we have it. Once texture is loaded, current state of the instance moves /// to Status.Loaded. /// </summary> internal void UpdatePreview() { switch (m_previewState) { case State.Loading: if (ProBuilderBridge.ProBuilderExists() && ProBuilderInterface.IsProBuilderObject(m_Asset as GameObject)) { m_PreviewTexture = GenerateProBuilderPreview(); } else if (PolyEditorUtility.IsPolybrushObject(m_Asset as GameObject)) { m_PreviewTexture = GeneratePreview(); } else { m_PreviewTexture = AssetPreview.GetAssetPreview(m_Asset); } if (previewTexture != null) { SetState(State.Loaded); } break; case State.Loaded: // Failsafe as AssetPreview can return white textures while loading // and erase that texture from memory once the right texture is available. if (m_PreviewTexture == null) { SetState(State.Loading); } break; } }
void OnDestroy() { SetTool(BrushTool.None); #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { ProBuilderBridge.SetSelectMode(ProBuilderBridge.SelectMode.Object); } #endif foreach (BrushMode m in modes) { GameObject.DestroyImmediate(m); } if (brushSettings != null) { GameObject.DestroyImmediate(brushSettings); } if (m_BrushEditor != null) { GameObject.DestroyImmediate(m_BrushEditor); } }
static bool MeshInstanceMatchesGameObject(Mesh mesh, GameObject go) { if (ProBuilderBridge.IsValidProBuilderMesh(go)) { return(true); } int gameObjectId = go.GetInstanceID(); int meshId = GetMeshId(mesh); // If the mesh id doesn't parse to an ID it's definitely not an instance if (meshId == -1) { return(false); } // If the mesh id matches the instance id, it's already a scene instance owned by this object. If doesn't match, // next check that the mesh id gameObject does not exist. If it does exist, that means this mesh was duplicated // and already belongs to another object in the scene. If it doesn't exist, then it just means that the GameObject // id was changed as a normal part of the GameObject lifecycle. if (meshId == gameObjectId) { return(true); } // If it is an instance, and the IDs don't match but no existing GameObject claims this mesh, claim it. if (EditorUtility.InstanceIDToObject(meshId) == null) { mesh.name = k_MeshInstancePrefix + go.GetInstanceID(); return(true); } // The mesh did not match the gameObject id, and the mesh id points to an already existing object in the scene. return(false); }
/// <summary> /// Tests if a GameObject is a ProBuilder mesh or not. /// </summary> /// <param name="gameObject"></param> /// <returns></returns> internal static bool IsProBuilderObject(GameObject gameObject) { if (ProBuilderBridge.ProBuilderExists()) { return(ProBuilderBridge.IsValidProBuilderMesh(gameObject)); } return(false); }
/// <summary> /// Tests if a GameObject is a ProBuilder mesh or not. /// </summary> /// <param name="gameObject"></param> /// <returns></returns> internal static bool IsProBuilderObject(GameObject gameObject) { #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { return(ProBuilderBridge.IsValidProBuilderMesh(gameObject)); } return(false); #endif }
internal override void OnBrushApply(BrushTarget brushTarget, BrushSettings brushSettings) { // false means no ToMesh or Refresh, true does. Optional addl bool runs pb_Object.Optimize() brushTarget.editableObject.Apply(true); if (ProBuilderBridge.ProBuilderExists() && brushTarget.editableObject.isProBuilderObject) { ProBuilderBridge.Refresh(brushTarget.gameObject); } UpdateTempComponent(brushTarget, brushSettings); }
/// <summary> /// Switch Polybrush to the given tool. /// </summary> /// <param name="brushTool">Tool to show in Polybrush window.</param> /// <param name="enableTool">If true, will activate the given tool automatically. Default: true.</param> internal void SetTool(BrushTool brushTool, bool enableTool = true) { if (brushTool == tool && mode != null) { return; } #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { ProBuilderBridge.SetSelectMode(ProBuilderBridge.SelectMode.Object); } #endif if (mode != null) { // Exiting edit mode if (m_LastHoveredGameObject != null) { OnBrushExit(m_LastHoveredGameObject); FinalizeAndResetHovering(); } mode.OnDisable(); } m_LastHoveredGameObject = null; System.Type modeType = brushTool.GetModeType(); if (modeType != null) { mode = modes.FirstOrDefault(x => x != null && x.GetType() == modeType); if (mode == null) { mode = (BrushMode)ScriptableObject.CreateInstance(modeType); } } // Handle tool auto activation/deactivation. tool = enableTool? brushTool : BrushTool.None; if (tool != BrushTool.None) { Tools.current = Tool.None; mode.OnEnable(); } EnsureBrushSettingsListIsValid(); DoRepaint(); }
void OnEnable() { if (!PrefUtility.VersionCheck()) { PrefUtility.ClearPrefs(); } PolybrushEditor.s_Instance = this; // Editor window setup titleContent = new GUIContent("Polybrush"); wantsMouseMove = true; minSize = k_EditorWindowMinimumSize; m_BrushMirrorEditor = new MirrorSettingsEditor(); #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { ProBuilderBridge.SubscribeToSelectModeChanged(OnProBuilderSelectModeChanged); } #endif m_GCToolmodeIcons = new GUIContent[] { EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/Sculpt"), "Sculpt on meshes"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/Smooth"), "Smooth mesh geometry"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/PaintVertexColors"), "Paint vertex colors on meshes"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/PaintPrefabs"), "Scatter Prefabs on meshes"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/PaintTextures"), "Paint textures on meshes"), }; #if UNITY_2019_1_OR_NEWER SceneView.duringSceneGui += OnSceneGUI; #else SceneView.onSceneGUIDelegate += OnSceneGUI; #endif Undo.undoRedoPerformed += UndoRedoPerformed; // force update the preview m_LastHoveredGameObject = null; EnsureBrushSettingsListIsValid(); PopulateAvailableBrushList(); SetTool(BrushTool.RaiseLower, false); Selection.selectionChanged -= OnSelectionChanged; Selection.selectionChanged += OnSelectionChanged; }
/// <summary> /// Generate a static preview for a ProBuilderMesh. /// </summary> /// <returns>Static preview of the given ProBuilderMesh.</returns> internal Texture2D GenerateProBuilderPreview() { GameObject copy = GameObject.Instantiate <GameObject>(m_Asset as GameObject); copy.hideFlags = HideFlags.HideAndDontSave; ProBuilderBridge.ToMesh(copy); ProBuilderBridge.Refresh(copy); MeshFilter mf = copy.GetComponent <MeshFilter>(); Editor meshEditor = Editor.CreateEditor(mf.sharedMesh); Texture2D preview = meshEditor.RenderStaticPreview(null, null, k_PreviewSize.x, k_PreviewSize.y); ScriptableObject.DestroyImmediate(meshEditor); ScriptableObject.DestroyImmediate(copy); return(preview); }
protected void UpdateWireframe(BrushTarget target, BrushSettings settings) { if (!Util.IsValid(target)) { return; } if (m_EditableObjectsData.TryGetValue(target.editableObject, out EditableObjectData data)) { if (data.TempComponent != null) { data.TempComponent.OnVerticesMoved(target.editableObject.editMesh); } //Might be costly to do that on every wireframe update if (ProBuilderBridge.ProBuilderExists() && target.editableObject.isProBuilderObject) { ProBuilderBridge.RefreshEditor(false); } } }
void FinalizeAndResetHovering() { foreach (var kvp in m_Hovering) { BrushTarget target = kvp.Value; if (!Util.IsValid(target)) { continue; } // if mesh hasn't been modified, revert it back // to the original mesh so that unnecessary assets // aren't allocated. if it has been modified, let // the editableObject apply those changes to the // pb_Object if necessary. if (!target.editableObject.isDirty) { target.editableObject.Revert(); } else { target.editableObject.Apply(true, true); } } m_Hovering.Clear(); brushTarget = null; m_LastHoveredGameObject = null; #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { ProBuilderBridge.RefreshEditor(false); } #endif Repaint(); }
internal override void UndoRedoPerformed(List <GameObject> modified) { modifiedMeshes = new HashSet <PolyMesh>(modifiedMeshes.Where(x => x != null)); #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { // delete & undo causes cases where object is not null but the reference to it's pb_Object is HashSet <GameObject> remove = new HashSet <GameObject>(); foreach (GameObject pb in modifiedPbMeshes) { try { ProBuilderBridge.ToMesh(pb); ProBuilderBridge.Refresh(pb); ProBuilderBridge.Optimize(pb); } catch { remove.Add(pb); } } if (remove.Count() > 0) { modifiedPbMeshes.SymmetricExceptWith(remove); } } #endif foreach (PolyMesh m in modifiedMeshes) { m.UpdateMeshFromData(); } base.UndoRedoPerformed(modified); }
/// <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; }
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(); }
void OnEnable() { if (!PrefUtility.VersionCheck()) { PrefUtility.ClearPrefs(); } PolyEditor.s_Instance = this; // Editor window setup titleContent = new GUIContent("Polybrush"); wantsMouseMove = true; minSize = k_EditorWindowMinimumSize; m_BrushMirrorEditor = new MirrorSettingsEditor(); #if PROBUILDER_4_0_OR_NEWER if (ProBuilderBridge.ProBuilderExists()) { ProBuilderBridge.SubscribeToSelectModeChanged(OnProBuilderSelectModeChanged); } #endif m_GCToolmodeIcons = new GUIContent[] { EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/Sculpt"), "Sculpt on meshes"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/Smooth"), "Smooth mesh geometry"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/PaintVertexColors"), "Paint vertex colors on meshes"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/PaintPrefabs"), "Scatter Prefabs on meshes"), EditorGUIUtility.TrIconContent(IconUtility.GetIcon("Toolbar/PaintTextures"), "Paint textures on meshes"), }; #if UNITY_2019_1_OR_NEWER SceneView.duringSceneGui += OnSceneGUI; #else SceneView.onSceneGUIDelegate += OnSceneGUI; #endif Undo.undoRedoPerformed += UndoRedoPerformed; // force update the preview m_LastHoveredGameObject = null; if (brushSettings == null) { if (brushSettingsAsset == null) { brushSettingsAsset = AssetDatabase.LoadAssetAtPath <BrushSettings>(AssetDatabase.GUIDToAssetPath(EditorPrefs.GetString(k_BrushSettingsAssetPref, ""))); } if (EditorPrefs.HasKey(k_BrushSettingsPref)) { brushSettings = ScriptableObject.CreateInstance <BrushSettings>(); JsonUtility.FromJsonOverwrite(EditorPrefs.GetString(k_BrushSettingsPref), brushSettings); if (EditorPrefs.HasKey(k_BrushSettingsName)) { brushSettings.name = EditorPrefs.GetString(k_BrushSettingsName); } } else { SetBrushSettings(brushSettingsAsset != null ? brushSettingsAsset : PolyEditorUtility.GetFirstOrNew <BrushSettings>()); } } RefreshAvailableBrushes(); SetTool(BrushTool.RaiseLower, false); Selection.selectionChanged -= OnSelectionChanged; Selection.selectionChanged += OnSelectionChanged; }