You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
5.3 KiB
C#
160 lines
5.3 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.ProBuilder;
|
|
using UnityEngine.ProBuilder.MeshOperations;
|
|
|
|
namespace UnityEditor.ProBuilder.Actions
|
|
{
|
|
sealed class DetachFaces : MenuAction
|
|
{
|
|
Pref<DetachSetting> m_DetachSetting = new Pref<DetachSetting>("DetachFaces.target", DetachSetting.GameObject);
|
|
|
|
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
|
public override Texture2D icon { get { return IconUtility.GetIcon("Toolbar/Face_Detach", IconSkin.Pro); } }
|
|
public override TooltipContent tooltip { get { return s_Tooltip; } }
|
|
|
|
static readonly TooltipContent s_Tooltip = new TooltipContent
|
|
(
|
|
"Detach Faces",
|
|
"Creates a new object (or submesh) from the selected faces."
|
|
);
|
|
|
|
public override SelectMode validSelectModes
|
|
{
|
|
get { return SelectMode.Face; }
|
|
}
|
|
|
|
public override bool enabled
|
|
{
|
|
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
|
}
|
|
|
|
protected override MenuActionState optionsMenuState
|
|
{
|
|
get { return MenuActionState.VisibleAndEnabled; }
|
|
}
|
|
|
|
enum DetachSetting
|
|
{
|
|
GameObject,
|
|
Submesh
|
|
};
|
|
|
|
protected override void OnSettingsGUI()
|
|
{
|
|
GUILayout.Label("Detach Face Settings", EditorStyles.boldLabel);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
m_DetachSetting.value = EditorGUILayout.Toggle("Separate GameObject", m_DetachSetting.value == DetachSetting.GameObject)
|
|
? DetachSetting.GameObject
|
|
: DetachSetting.Submesh;
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
ProBuilderSettings.Save();
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button("Detach Selection"))
|
|
EditorUtility.ShowNotification(PerformAction().notification);
|
|
}
|
|
|
|
protected override ActionResult PerformActionImplementation()
|
|
{
|
|
if (MeshSelection.selectedObjectCount < 1)
|
|
return ActionResult.NoSelection;
|
|
|
|
UndoUtility.RecordSelection("Detach Face(s)");
|
|
|
|
if (m_DetachSetting == DetachSetting.GameObject)
|
|
return DetachFacesToObject();
|
|
|
|
return DetachFacesToSubmesh();
|
|
}
|
|
|
|
static ActionResult DetachFacesToSubmesh()
|
|
{
|
|
int count = 0;
|
|
|
|
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
|
{
|
|
pb.ToMesh();
|
|
List<Face> res = pb.DetachFaces(pb.selectedFacesInternal);
|
|
pb.Refresh();
|
|
pb.Optimize();
|
|
|
|
pb.SetSelectedFaces(res.ToArray());
|
|
|
|
count += pb.selectedFaceCount;
|
|
}
|
|
|
|
ProBuilderEditor.Refresh();
|
|
|
|
if (count > 0)
|
|
return new ActionResult(ActionResult.Status.Success, "Detach " + count + (count > 1 ? " Faces" : " Face"));
|
|
|
|
return new ActionResult(ActionResult.Status.Success, "Detach Faces");
|
|
}
|
|
|
|
static ActionResult DetachFacesToObject()
|
|
{
|
|
int detachedFaceCount = 0;
|
|
List<GameObject> detached = new List<GameObject>();
|
|
|
|
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
|
{
|
|
if (mesh.selectedFaceCount < 1 || mesh.selectedFaceCount == mesh.facesInternal.Length)
|
|
continue;
|
|
|
|
var primary = mesh.selectedFaceIndexes;
|
|
detachedFaceCount += primary.Count;
|
|
|
|
List<int> inverse = new List<int>();
|
|
|
|
for (int i = 0; i < mesh.facesInternal.Length; i++)
|
|
if (!primary.Contains(i))
|
|
inverse.Add(i);
|
|
|
|
ProBuilderMesh copy = Object.Instantiate(mesh.gameObject, mesh.transform.parent).GetComponent<ProBuilderMesh>();
|
|
EditorUtility.SynchronizeWithMeshFilter(copy);
|
|
|
|
if (copy.transform.childCount > 0)
|
|
{
|
|
for (int i = copy.transform.childCount - 1; i > -1; i--)
|
|
Object.DestroyImmediate(copy.transform.GetChild(i).gameObject);
|
|
|
|
foreach (var child in mesh.transform.GetComponentsInChildren<ProBuilderMesh>())
|
|
EditorUtility.SynchronizeWithMeshFilter(child);
|
|
}
|
|
|
|
Undo.RegisterCreatedObjectUndo(copy.gameObject, "Detach Selection");
|
|
|
|
mesh.DeleteFaces(primary);
|
|
copy.DeleteFaces(inverse);
|
|
|
|
mesh.Rebuild();
|
|
copy.Rebuild();
|
|
|
|
mesh.Optimize();
|
|
copy.Optimize();
|
|
|
|
mesh.ClearSelection();
|
|
copy.ClearSelection();
|
|
|
|
copy.SetSelectedFaces(copy.faces);
|
|
|
|
copy.gameObject.name = GameObjectUtility.GetUniqueNameForSibling(mesh.transform.parent, mesh.gameObject.name); ;
|
|
detached.Add(copy.gameObject);
|
|
}
|
|
|
|
MeshSelection.SetSelection(detached);
|
|
ProBuilderEditor.Refresh();
|
|
|
|
if (detachedFaceCount > 0)
|
|
return new ActionResult(ActionResult.Status.Success, "Detach " + detachedFaceCount + " faces to new Object");
|
|
|
|
return new ActionResult(ActionResult.Status.Failure, "No Faces Selected");
|
|
}
|
|
}
|
|
}
|