Added player character and step in/out of dialogue in scene 101
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class BevelEdges : MenuAction
|
||||
{
|
||||
const float k_MinBevelDistance = .0001f;
|
||||
Pref<float> m_BevelSize = new Pref<float>("BevelEdges.size", .2f);
|
||||
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Edge_Bevel";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return s_Tooltip; } }
|
||||
|
||||
static readonly GUIContent gc_BevelDistance = EditorGUIUtility.TrTextContent("Distance", "The size of the bevel in meters. The value is clamped to the size of the smallest affected face.");
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Bevel Edges",
|
||||
@"Smooth the selected edges by adding a slanted face connecting the two adjacent faces."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge | SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedEdgeCount > 0; }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var floatField = new FloatField(gc_BevelDistance.text);
|
||||
floatField.tooltip = gc_BevelDistance.tooltip;
|
||||
floatField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
floatField.SetValueWithoutNotify(m_BevelSize.value);
|
||||
floatField.RegisterCallback<ChangeEvent<float>>(evt =>
|
||||
{
|
||||
if (m_BevelSize.value != evt.newValue)
|
||||
{
|
||||
if (evt.newValue < k_MinBevelDistance)
|
||||
{
|
||||
m_BevelSize.SetValue(k_MinBevelDistance);
|
||||
floatField.SetValueWithoutNotify(m_BevelSize);
|
||||
}
|
||||
else
|
||||
m_BevelSize.SetValue(evt.newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
});
|
||||
|
||||
System.Action onDelayedPreviewChanged = () =>
|
||||
{
|
||||
floatField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
};
|
||||
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
|
||||
root.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||
{
|
||||
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
|
||||
});
|
||||
root.Add(floatField);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Bevel Edge Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("Amount determines how much space the bevel occupies. The value is clamped to the size of the smallest affected face.", MessageType.Info);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_BevelSize.value = EditorGUILayout.FloatField(gc_BevelDistance, m_BevelSize);
|
||||
|
||||
if (m_BevelSize < k_MinBevelDistance)
|
||||
m_BevelSize.value = k_MinBevelDistance;
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Bevel Edges"))
|
||||
PerformAction();
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
ActionResult res = ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Bevel Edges");
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
pb.ToMesh();
|
||||
|
||||
List<Face> faces = Bevel.BevelEdges(pb, pb.selectedEdges, m_BevelSize);
|
||||
res = faces != null ? new ActionResult(ActionResult.Status.Success, "Bevel Edges") : new ActionResult(ActionResult.Status.Failure, "Failed Bevel Edges");
|
||||
|
||||
if (res)
|
||||
pb.SetSelectedFaces(faces);
|
||||
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7bd43b2e3e547199420eec1d903ff30
|
||||
timeCreated: 1466191810
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class BridgeEdges : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Edge_Bridge";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Bridge Edges",
|
||||
@"Add a new face connecting two edges.",
|
||||
keyCommandAlt, 'B'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.topInternal.Any(x => x.selectedEdgeCount == 2); }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Bridge Edges");
|
||||
|
||||
bool success = false;
|
||||
|
||||
foreach (var mesh in MeshSelection.topInternal)
|
||||
{
|
||||
if (mesh.selectedEdgeCount == 2)
|
||||
{
|
||||
if (mesh.Bridge(mesh.selectedEdges[0], mesh.selectedEdges[1], ProBuilderEditor.s_AllowNonManifoldActions) != null)
|
||||
{
|
||||
success = true;
|
||||
mesh.ToMesh();
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
ProBuilderEditor.Refresh();
|
||||
return new ActionResult(ActionResult.Status.Success, "Bridge Edges");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Failed Bridge Edges. Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold).");
|
||||
return new ActionResult(ActionResult.Status.Failure, "Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 066d50a96f4a480fac834bf3b64a8c26
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class CollapseVertices : MenuAction
|
||||
{
|
||||
Pref<bool> m_CollapseToFirst = new Pref<bool>("CollapseVertices.collapseToFirst", false);
|
||||
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Vert_Collapse";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Collapse Vertices",
|
||||
@"Merge all selected vertices into a single vertex, centered at the first vertex or average position of all selected points."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Vertex; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedSharedVertexCountObjectMax > 1; }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
root.style.minWidth = 150;
|
||||
|
||||
var toggle = new Toggle("Collapse to First");
|
||||
toggle.tooltip = "Collapse To First setting decides where the collapsed vertex will be placed. If True, " +
|
||||
"the new vertex will be placed at the position of the first selected vertex. If false, the new vertex " +
|
||||
"is placed at the average position of all selected vertices.";
|
||||
toggle.SetValueWithoutNotify(m_CollapseToFirst);
|
||||
toggle.RegisterCallback<ChangeEvent<bool>>(evt =>
|
||||
{
|
||||
m_CollapseToFirst.SetValue(evt.newValue);
|
||||
|
||||
PreviewActionManager.UpdatePreview();
|
||||
});
|
||||
root.Add(toggle);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Collapse Vertices Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("Collapse To First setting decides where the collapsed vertex will be placed.\n\nIf True, the new vertex will be placed at the position of the first selected vertex. If false, the new vertex is placed at the average position of all selected vertices.", MessageType.Info);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_CollapseToFirst.value = EditorGUILayout.Toggle("Collapse To First", m_CollapseToFirst);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Collapse Vertices"))
|
||||
PerformAction();
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
bool success = false;
|
||||
|
||||
bool collapseToFirst = m_CollapseToFirst;
|
||||
|
||||
UndoUtility.RecordSelection("Collapse Vertices");
|
||||
|
||||
foreach (var mesh in MeshSelection.topInternal)
|
||||
{
|
||||
if (mesh.selectedIndexesInternal.Length > 1)
|
||||
{
|
||||
int newIndex = mesh.MergeVertices(mesh.selectedIndexesInternal, collapseToFirst);
|
||||
|
||||
success = newIndex > -1;
|
||||
|
||||
if (success)
|
||||
mesh.SetSelectedVertices(new int[] { newIndex });
|
||||
|
||||
mesh.ToMesh();
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
}
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (success)
|
||||
return new ActionResult(ActionResult.Status.Success, "Collapse Vertices");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, "Collapse Vertices\nNo Vertices Selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d1e3d0e26dcfcb438a45fbbf9f23e92
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ProBuilder.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEditor.ProBuilder;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class ConformFaceNormals : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Face_ConformNormals";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return s_TooltipContent; } }
|
||||
public override string menuTitle { get { return "Conform Face Normals"; } }
|
||||
|
||||
static readonly TooltipContent s_TooltipContent = new TooltipContent
|
||||
(
|
||||
"Conform Face Normals",
|
||||
@"Orients all selected faces to face the same direction."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCountObjectMax > 1; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
var selection = MeshSelection.topInternal;
|
||||
|
||||
UndoUtility.RecordSelection("Conform " + (MeshSelection.selectedFaceCount > 0 ? "Face" : "Object") + " Normals.");
|
||||
|
||||
ActionResult res = ActionResult.NoSelection;
|
||||
|
||||
foreach (ProBuilderMesh pb in selection)
|
||||
{
|
||||
var faces = pb.GetSelectedFaces();
|
||||
|
||||
if (faces == null)
|
||||
continue;
|
||||
|
||||
res = UnityEngine.ProBuilder.MeshOperations.SurfaceTopology.ConformNormals(pb, faces);
|
||||
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fdc9faa599347a3b697e785ac4719b3
|
||||
timeCreated: 1457564636
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class ConnectEdges : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Edge_Connect";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return s_Tooltip; } }
|
||||
protected internal override bool hasFileMenuEntry { get { return false; } }
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Connect Edges",
|
||||
"Inserts a new edge connecting the center points of all selected edges. See also \"Subdivide.\"",
|
||||
keyCommandAlt, 'E'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedEdgeCountObjectMax > 1; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
ActionResult res = ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Connect Edges");
|
||||
|
||||
foreach (var mesh in MeshSelection.topInternal)
|
||||
{
|
||||
Edge[] connections;
|
||||
Face[] faces;
|
||||
|
||||
res = ConnectElements.Connect(mesh, mesh.selectedEdges, out faces, out connections, true, true);
|
||||
|
||||
if (connections != null)
|
||||
{
|
||||
if (connections.Length != 0)
|
||||
mesh.SetSelectedEdges(connections);
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
}
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9eda47df4ab94d8a8174832a11359a8a
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class ConnectVertices : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Vert_Connect";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return _tooltip; } }
|
||||
protected internal override bool hasFileMenuEntry { get { return false; } }
|
||||
|
||||
static readonly TooltipContent _tooltip = new TooltipContent
|
||||
(
|
||||
"Connect Vertices",
|
||||
@"Adds edges connecting all selected vertices.",
|
||||
keyCommandAlt, 'E'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Vertex; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedSharedVertexCount > 1; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
ActionResult res = ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Connect Vertices");
|
||||
|
||||
foreach (var mesh in MeshSelection.topInternal)
|
||||
{
|
||||
mesh.ToMesh();
|
||||
int[] splits = mesh.Connect(mesh.selectedIndexesInternal);
|
||||
|
||||
if (splits != null)
|
||||
{
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
mesh.SetSelectedVertices(splits);
|
||||
res = new ActionResult(ActionResult.Status.Success, "Connect Edges");
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ActionResult(ActionResult.Status.Failure, "Failed Connecting Edges");
|
||||
}
|
||||
}
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6d73444bb8cc9b468d5ff190d7de759
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class DeleteFaces : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_Delete";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Delete Faces",
|
||||
@"Delete all selected faces.",
|
||||
keyCommandDelete
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Delete Face");
|
||||
|
||||
int count = 0;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
if (pb.selectedFaceCount == pb.facesInternal.Length)
|
||||
{
|
||||
Debug.LogWarning("Attempting to delete all faces on this mesh... I'm afraid I can't let you do that.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Undo.RecordObject(pb.renderer, "Update Renderer");
|
||||
|
||||
pb.DeleteFaces(pb.selectedFacesInternal);
|
||||
count += pb.selectedFaceCount;
|
||||
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
}
|
||||
|
||||
MeshSelection.ClearElementSelection();
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (count > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Delete " + count + " Faces");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, "No Faces Selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0c8bf4cfa4d461cb01b12bb9e3e42f9
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using System;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
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 string iconPath => "Toolbar/Face_Detach";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
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
|
||||
};
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var detachFace = new EnumField("Detach To", m_DetachSetting);
|
||||
detachFace.tooltip = "You can create a new Game Object with the selected face(s), or keep them as part of this object by using a Submesh.";
|
||||
detachFace.RegisterCallback<ChangeEvent<string>>(evt =>
|
||||
{
|
||||
Enum.TryParse(evt.newValue, out DetachSetting newValue);
|
||||
m_DetachSetting.SetValue(newValue);
|
||||
});
|
||||
root.Add(detachFace);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
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>();
|
||||
copy.MakeUnique();
|
||||
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.RecordObject(mesh.renderer, "Update Renderer");
|
||||
|
||||
mesh.DeleteFaces(primary);
|
||||
copy.DeleteFaces(inverse);
|
||||
|
||||
mesh.Rebuild();
|
||||
copy.Rebuild();
|
||||
|
||||
mesh.Optimize();
|
||||
copy.Optimize();
|
||||
|
||||
mesh.ClearSelection();
|
||||
copy.ClearSelection();
|
||||
|
||||
copy.SetSelectedFaces(copy.faces);
|
||||
|
||||
Undo.RegisterCreatedObjectUndo(copy.gameObject, "Detach Selection");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9930631c9404fb0b63a91644558eb39
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class DuplicateFaces : MenuAction
|
||||
{
|
||||
Pref<DuplicateFaceSetting> m_DuplicateFaceSetting = new Pref<DuplicateFaceSetting>("DuplicateFaces.target", DuplicateFaceSetting.GameObject);
|
||||
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Face_Duplicate";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return s_Tooltip; } }
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Duplicate Faces",
|
||||
"Makes an exact copy of the selected faces, and either adds them to this mesh or creates a new Game Object"
|
||||
);
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
internal enum DuplicateFaceSetting
|
||||
{
|
||||
GameObject,
|
||||
Submesh
|
||||
};
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var duplicateType = new EnumField("Duplicate To", m_DuplicateFaceSetting);
|
||||
duplicateType.tooltip = "You can create a new Game Object with the selected face(s), or keep them as part of this object by using a Submesh.";
|
||||
duplicateType.RegisterCallback<ChangeEvent<string>>(evt =>
|
||||
{
|
||||
Enum.TryParse(evt.newValue, out DuplicateFaceSetting newValue);
|
||||
if (m_DuplicateFaceSetting.value == newValue)
|
||||
return;
|
||||
|
||||
m_DuplicateFaceSetting.value = newValue;
|
||||
ProBuilderSettings.Save();
|
||||
});
|
||||
root.Add(duplicateType);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Duplicate Face Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("You can create a new Game Object with the selected face(s), or keep them as part of this object by using a Submesh.", MessageType.Info);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_DuplicateFaceSetting.value = (DuplicateFaceSetting)EditorGUILayout.EnumPopup("Duplicate To", m_DuplicateFaceSetting);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Duplicate Selection"))
|
||||
EditorUtility.ShowNotification(PerformAction().notification);
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Duplicate Face(s)");
|
||||
|
||||
if (m_DuplicateFaceSetting == DuplicateFaceSetting.GameObject)
|
||||
return DuplicateFacesToObject();
|
||||
|
||||
return DuplicateFacesToSubmesh();
|
||||
}
|
||||
|
||||
static ActionResult DuplicateFacesToSubmesh()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
pb.ToMesh();
|
||||
List<Face> res = pb.DetachFaces(pb.selectedFacesInternal, false);
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
|
||||
pb.SetSelectedFaces(res.ToArray());
|
||||
|
||||
count += pb.selectedFaceCount;
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (count > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Duplicate " + count + (count > 1 ? " Faces" : " Face"));
|
||||
|
||||
return new ActionResult(ActionResult.Status.Success, "Duplicate Faces");
|
||||
}
|
||||
|
||||
static ActionResult DuplicateFacesToObject()
|
||||
{
|
||||
int duplicatedFaceCount = 0;
|
||||
List<GameObject> duplicated = new List<GameObject>();
|
||||
|
||||
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
||||
{
|
||||
if (mesh.selectedFaceCount < 1)
|
||||
continue;
|
||||
|
||||
var primary = mesh.selectedFaceIndexes;
|
||||
duplicatedFaceCount += 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>();
|
||||
copy.MakeUnique();
|
||||
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, "Duplicate Selection");
|
||||
|
||||
copy.DeleteFaces(inverse);
|
||||
copy.Rebuild();
|
||||
copy.Optimize();
|
||||
mesh.ClearSelection();
|
||||
copy.ClearSelection();
|
||||
copy.SetSelectedFaces(copy.faces);
|
||||
|
||||
copy.gameObject.name = GameObjectUtility.GetUniqueNameForSibling(mesh.transform.parent, mesh.gameObject.name);
|
||||
duplicated.Add(copy.gameObject);
|
||||
}
|
||||
|
||||
MeshSelection.SetSelection(duplicated);
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (duplicatedFaceCount > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Duplicate " + duplicatedFaceCount + " faces to new Object");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, "No Faces Selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 172d21c82259a0c428b3924edd908358
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEditor.ProBuilder;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
// The menu bar entry for Extrude delegates to Face/Edge as appropriate.
|
||||
sealed class Extrude : MenuAction
|
||||
{
|
||||
public override ToolbarGroup @group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => String.Empty;
|
||||
public override Texture2D icon => null;
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Extrude", "",
|
||||
keyCommandSuper, 'E'
|
||||
);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face | SelectMode.Edge; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ProBuilderEditor.selectMode)
|
||||
{
|
||||
case SelectMode.Edge:
|
||||
return EditorToolbarLoader.GetInstance<ExtrudeEdges>().enabled;
|
||||
case SelectMode.Face:
|
||||
return EditorToolbarLoader.GetInstance<ExtrudeFaces>().enabled;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool hidden
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
switch (ProBuilderEditor.selectMode)
|
||||
{
|
||||
case SelectMode.Edge:
|
||||
return EditorToolbarLoader.GetInstance<ExtrudeEdges>().PerformAction();
|
||||
case SelectMode.Face:
|
||||
return EditorToolbarLoader.GetInstance<ExtrudeFaces>().PerformAction();
|
||||
|
||||
default:
|
||||
return ActionResult.NoSelection;
|
||||
}
|
||||
}
|
||||
|
||||
internal override string GetMenuItemOverride()
|
||||
{
|
||||
return @" switch (ProBuilderEditor.selectMode)
|
||||
{
|
||||
case SelectMode.Edge:
|
||||
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<ExtrudeEdges>(), true));
|
||||
break;
|
||||
case SelectMode.Face:
|
||||
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<ExtrudeFaces>(), true));
|
||||
break;
|
||||
}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b24935f60532d7428cbcb5df316e359
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class ExtrudeEdges : MenuAction
|
||||
{
|
||||
Pref<float> m_ExtrudeEdgeDistance = new Pref<float>("ExtrudeEdges.distance", .5f);
|
||||
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Edge_Extrude";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return s_Tooltip; } }
|
||||
protected internal override bool hasFileMenuEntry { get { return false; } }
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Extrude Edges",
|
||||
@"Adds a new face extending from the currently selected edges. Edges must have an open side to be extruded.
|
||||
NB : Allow non-manifold actions should be authorized in ProBuilder preferences to enable this action.",
|
||||
keyCommandSuper, 'E'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedEdgeCount > 0 && ProBuilderEditor.s_AllowNonManifoldActions; }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var toggle = new Toggle("As Group");
|
||||
toggle.tooltip = "Extrude as Group determines whether or not adjacent faces stay attached to one another when extruding.";
|
||||
toggle.SetValueWithoutNotify(VertexManipulationTool.s_ExtrudeEdgesAsGroup);
|
||||
toggle.RegisterCallback<ChangeEvent<bool>>(OnEdgesAsGroupChanged);
|
||||
root.Add(toggle);
|
||||
|
||||
var floatField = new FloatField("Distance");
|
||||
floatField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
floatField.tooltip = "Extrude Amount determines how far an edge will be moved along it's normal when extruding. This value can be negative.";
|
||||
floatField.SetValueWithoutNotify(m_ExtrudeEdgeDistance);
|
||||
floatField.RegisterCallback<ChangeEvent<float>>(OnExtrudeChanged);
|
||||
|
||||
System.Action onDelayedPreviewChanged = () =>
|
||||
{
|
||||
floatField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
};
|
||||
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
|
||||
root.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||
{
|
||||
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
|
||||
});
|
||||
root.Add(floatField);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void OnEdgesAsGroupChanged(ChangeEvent<bool> evt)
|
||||
{
|
||||
VertexManipulationTool.s_ExtrudeEdgesAsGroup.SetValue(evt.newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
|
||||
void OnExtrudeChanged(ChangeEvent<float> evt)
|
||||
{
|
||||
m_ExtrudeEdgeDistance.SetValue(evt.newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Extrude Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("Extrude Amount determines how far an edge will be moved along it's normal when extruding. This value can be negative.\n\nExtrude as Group determines whether or not adjacent faces stay attached to one another when extruding.", MessageType.Info);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
VertexManipulationTool.s_ExtrudeEdgesAsGroup.value = EditorGUILayout.Toggle("As Group", VertexManipulationTool.s_ExtrudeEdgesAsGroup);
|
||||
|
||||
m_ExtrudeEdgeDistance.value = EditorGUILayout.FloatField("Distance", m_ExtrudeEdgeDistance);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Extrude Edges"))
|
||||
PerformAction();
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Extrude");
|
||||
|
||||
int extrudedFaceCount = 0;
|
||||
bool success = false;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
pb.ToMesh();
|
||||
pb.Refresh(RefreshMask.Normals);
|
||||
|
||||
if (pb.selectedEdgeCount < 1)
|
||||
continue;
|
||||
|
||||
extrudedFaceCount += pb.selectedEdgeCount;
|
||||
|
||||
Edge[] newEdges = pb.Extrude(pb.selectedEdges,
|
||||
m_ExtrudeEdgeDistance,
|
||||
VertexManipulationTool.s_ExtrudeEdgesAsGroup,
|
||||
ProBuilderEditor.s_AllowNonManifoldActions);
|
||||
|
||||
success |= newEdges != null;
|
||||
|
||||
if (success)
|
||||
pb.SetSelectedEdges(newEdges);
|
||||
else
|
||||
extrudedFaceCount -= pb.selectedEdgeCount;
|
||||
|
||||
pb.Rebuild();
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (extrudedFaceCount > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Extrude");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Canceled, "Extrude\nEmpty Selection");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97626f417dcf4f68a68db312995f9e6b
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class ExtrudeFaces : MenuAction
|
||||
{
|
||||
Pref<float> m_ExtrudeDistance = new Pref<float>("ExtrudeFaces.distance", .5f);
|
||||
|
||||
ExtrudeMethod extrudeMethod
|
||||
{
|
||||
get { return VertexManipulationTool.s_ExtrudeMethod; }
|
||||
set { VertexManipulationTool.s_ExtrudeMethod.value = value; }
|
||||
}
|
||||
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_Extrude";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
protected internal override bool hasFileMenuEntry
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
Texture2D[] m_Icons = null;
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Extrude Faces",
|
||||
"Extrude selected faces, either as a group or individually.\n\nAlt + Click this button to show additional Extrude options.",
|
||||
keyCommandSuper, 'E'
|
||||
);
|
||||
|
||||
public ExtrudeFaces()
|
||||
{
|
||||
m_Icons = new Texture2D[3];
|
||||
m_Icons[(int)ExtrudeMethod.IndividualFaces] = IconUtility.GetIcon("Toolbar/ExtrudeFace_Individual");
|
||||
m_Icons[(int)ExtrudeMethod.VertexNormal] = IconUtility.GetIcon("Toolbar/ExtrudeFace_VertexNormals");
|
||||
m_Icons[(int)ExtrudeMethod.FaceNormal] = IconUtility.GetIcon("Toolbar/ExtrudeFace_FaceNormals");
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var extrudeMethodLabel = new Label();
|
||||
extrudeMethodLabel.tooltip = " You may also choose to Extrude by Face Normal, Vertex Normal, or as Individual Faces.";
|
||||
extrudeMethodLabel.style.backgroundImage = m_Icons[(int)extrudeMethod];
|
||||
extrudeMethodLabel.style.height = 22;
|
||||
extrudeMethodLabel.style.width = 36;
|
||||
|
||||
var extrudeMethodField = new EnumField("Extrude By", extrudeMethod);
|
||||
extrudeMethodField.tooltip = " You may also choose to Extrude by Face Normal, Vertex Normal, or as Individual Faces.";
|
||||
extrudeMethodField.RegisterCallback<ChangeEvent<System.Enum>>(evt =>
|
||||
{
|
||||
var newEnumValue = (ExtrudeMethod)evt.newValue;
|
||||
if (extrudeMethod != newEnumValue)
|
||||
{
|
||||
extrudeMethod = newEnumValue;
|
||||
extrudeMethodLabel.style.backgroundImage = m_Icons[(int)extrudeMethod];
|
||||
ProBuilderSettings.Save();
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
});
|
||||
extrudeMethodField.style.flexGrow = 1;
|
||||
var line = new VisualElement();
|
||||
line.style.flexDirection = FlexDirection.Row;
|
||||
line.Add(extrudeMethodField);
|
||||
line.Add(extrudeMethodLabel);
|
||||
root.Add(line);
|
||||
|
||||
var distanceField = new FloatField("Distance");
|
||||
distanceField.tooltip = "Extrude Amount determines how far a face will be moved along it's normal when extruding. This value can be negative.";
|
||||
distanceField.SetValueWithoutNotify(m_ExtrudeDistance.value);
|
||||
distanceField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
distanceField.RegisterCallback<ChangeEvent<float>>(evt =>
|
||||
{
|
||||
m_ExtrudeDistance.SetValue(evt.newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
});
|
||||
root.Add(distanceField);
|
||||
|
||||
System.Action onDelayedPreviewChanged = () =>
|
||||
{
|
||||
distanceField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
};
|
||||
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
|
||||
root.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||
{
|
||||
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Extrude Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("Extrude Amount determines how far a face will be moved along it's normal when extruding. This value can be negative.\n\nYou may also choose to Extrude by Face Normal, Vertex Normal, or as Individual Faces.", MessageType.Info);
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Label(m_Icons[(int)extrudeMethod]);
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
extrudeMethod = (ExtrudeMethod)EditorGUILayout.EnumPopup("Extrude By", extrudeMethod);
|
||||
m_ExtrudeDistance.value = EditorGUILayout.FloatField("Distance", m_ExtrudeDistance);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Extrude Faces"))
|
||||
PerformAction();
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Extrude");
|
||||
|
||||
int extrudedFaceCount = 0;
|
||||
|
||||
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
||||
{
|
||||
mesh.ToMesh();
|
||||
mesh.Refresh(RefreshMask.Normals);
|
||||
|
||||
if (mesh.selectedFaceCount < 1)
|
||||
continue;
|
||||
|
||||
extrudedFaceCount += mesh.selectedFaceCount;
|
||||
var selectedFaces = mesh.GetSelectedFaces();
|
||||
|
||||
mesh.Extrude(selectedFaces,
|
||||
VertexManipulationTool.s_ExtrudeMethod,
|
||||
m_ExtrudeDistance);
|
||||
|
||||
mesh.SetSelectedFaces(selectedFaces);
|
||||
|
||||
mesh.Rebuild();
|
||||
mesh.Optimize();
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (extrudedFaceCount > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Extrude");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Canceled, "Extrude\nEmpty Selection");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d790e5dd7e634bac936d5dc0e026ae20
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ProBuilder.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class FillHole : MenuAction
|
||||
{
|
||||
Pref<bool> m_SelectEntirePath = new Pref<bool>("FillHole.selectEntirePath", true);
|
||||
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Edge_FillHole";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Fill Hole",
|
||||
@"Create a new face connecting all selected vertices."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge | SelectMode.Vertex; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && (MeshSelection.selectedEdgeCount > 0 || MeshSelection.selectedSharedVertexCount > 0); }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var toggle = new Toggle("Fill Entire Hole");
|
||||
toggle.tooltip = "Fill Hole can optionally fill entire holes (default) or just the selected vertices on the hole edges. If no elements are selected, the entire object will be scanned for holes.";
|
||||
toggle.style.flexGrow =1;
|
||||
toggle.SetValueWithoutNotify(m_SelectEntirePath);
|
||||
toggle.RegisterCallback<ChangeEvent<bool>>(OnFillHoleToggleChanged);
|
||||
root.Add(toggle);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void OnFillHoleToggleChanged(ChangeEvent<bool> evt)
|
||||
{
|
||||
m_SelectEntirePath.SetValue(evt.newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Fill Hole Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("Fill Hole can optionally fill entire holes (default) or just the selected vertices on the hole edges.\n\nIf no elements are selected, the entire object will be scanned for holes.", MessageType.Info);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_SelectEntirePath.value = EditorGUILayout.Toggle("Fill Entire Hole", m_SelectEntirePath);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Fill Hole"))
|
||||
EditorUtility.ShowNotification(PerformAction().notification);
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Fill Hole");
|
||||
|
||||
ActionResult res = new ActionResult(ActionResult.Status.NoChange, "No Holes Found");
|
||||
int filled = 0;
|
||||
bool wholePath = m_SelectEntirePath;
|
||||
|
||||
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
||||
{
|
||||
bool selectAll = mesh.selectedIndexesInternal == null || mesh.selectedIndexesInternal.Length < 1;
|
||||
IEnumerable<int> indexes = selectAll ? mesh.facesInternal.SelectMany(x => x.indexes) : mesh.selectedIndexesInternal;
|
||||
|
||||
mesh.ToMesh();
|
||||
|
||||
List<WingedEdge> wings = WingedEdge.GetWingedEdges(mesh);
|
||||
HashSet<int> common = mesh.GetSharedVertexHandles(indexes);
|
||||
List<List<WingedEdge>> holes = ElementSelection.FindHoles(wings, common);
|
||||
|
||||
HashSet<Face> appendedFaces = new HashSet<Face>();
|
||||
|
||||
foreach (List<WingedEdge> hole in holes)
|
||||
{
|
||||
List<int> holeIndexes;
|
||||
Face face;
|
||||
|
||||
if (wholePath)
|
||||
{
|
||||
// if selecting whole path and in edge mode, make sure the path contains
|
||||
// at least one complete edge from the selection.
|
||||
if (ProBuilderEditor.selectMode == SelectMode.Edge &&
|
||||
!hole.Any(x => common.Contains(x.edge.common.a) &&
|
||||
common.Contains(x.edge.common.b)))
|
||||
continue;
|
||||
|
||||
holeIndexes = hole.Select(x => x.edge.local.a).ToList();
|
||||
face = AppendElements.CreatePolygon(mesh, holeIndexes, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<WingedEdge> selected = hole.Where(x => common.Contains(x.edge.common.a));
|
||||
holeIndexes = selected.Select(x => x.edge.local.a).ToList();
|
||||
face = AppendElements.CreatePolygon(mesh, holeIndexes, true);
|
||||
}
|
||||
|
||||
if (face != null)
|
||||
{
|
||||
filled++;
|
||||
appendedFaces.Add(face);
|
||||
}
|
||||
}
|
||||
|
||||
mesh.SetSelectedFaces(appendedFaces);
|
||||
|
||||
wings = WingedEdge.GetWingedEdges(mesh);
|
||||
|
||||
// make sure the appended faces match the first adjacent face found
|
||||
// both in winding and face properties
|
||||
foreach (var appendedFace in appendedFaces)
|
||||
{
|
||||
var wing = wings.FirstOrDefault(x => x.face == appendedFace);
|
||||
|
||||
if (wing == null)
|
||||
continue;
|
||||
|
||||
using (var it = new WingedEdgeEnumerator(wing))
|
||||
{
|
||||
while (it.MoveNext())
|
||||
{
|
||||
if (it.Current == null)
|
||||
continue;
|
||||
|
||||
var currentWing = it.Current;
|
||||
var oppositeFace = it.Current.opposite != null ? it.Current.opposite.face : null;
|
||||
|
||||
if (oppositeFace != null && !appendedFaces.Contains(oppositeFace))
|
||||
{
|
||||
currentWing.face.submeshIndex = oppositeFace.submeshIndex;
|
||||
currentWing.face.uv = new AutoUnwrapSettings(oppositeFace.uv);
|
||||
SurfaceTopology.ConformOppositeNormal(currentWing.opposite);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh.ToMesh();
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (filled > 0)
|
||||
res = new ActionResult(ActionResult.Status.Success, filled > 1 ? string.Format("Filled {0} Holes", filled) : "Fill Hole");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2871f338d8894523bcde8a078df337c2
|
||||
timeCreated: 1466443265
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class FlipFaceEdge : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_FlipTri";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Flip Face Edge",
|
||||
@"Reverses the direction of the middle edge in a quad. Use this to fix ridges in quads with varied height corners."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Flip Face Edges");
|
||||
int success = 0;
|
||||
int attempts = 0;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
foreach (Face face in pb.selectedFacesInternal)
|
||||
{
|
||||
if (pb.FlipEdge(face))
|
||||
success++;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (success > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Flipped " + success + " Edges");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, string.Format("Flip Edges\n{0}", attempts > 0 ? "Faces Must Be Quads" : "No Faces Selected"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e7e1880876747d1823604bc506e3772
|
||||
timeCreated: 1457564632
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ProBuilder.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEditor.ProBuilder;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class FlipFaceNormals : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_FlipNormals";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Flip Face Normals",
|
||||
@"Reverses the direction of all faces in selection.",
|
||||
keyCommandAlt, 'N'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Flip Face Normals");
|
||||
|
||||
int c = 0;
|
||||
int faceCount = MeshSelection.selectedFaceCount;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
if (pb.selectedFaceCount < 1 && faceCount < 1)
|
||||
{
|
||||
foreach (var face in pb.facesInternal)
|
||||
face.Reverse();
|
||||
|
||||
c += pb.facesInternal.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var face in pb.GetSelectedFaces())
|
||||
face.Reverse();
|
||||
|
||||
c += pb.selectedFaceCount;
|
||||
}
|
||||
|
||||
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
}
|
||||
|
||||
if (c > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Flip " + c + (c > 1 ? " Face Normals" : " Face Normal"));
|
||||
|
||||
return new ActionResult(ActionResult.Status.Canceled, "Flip Normals\nNo Faces Selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71cfb4d65045470facb2a2e1e41bec03
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ProBuilder.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEditor.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class InsertEdgeLoop : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Edge_InsertLoop";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return _tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent _tooltip = new TooltipContent
|
||||
(
|
||||
"Insert Edge Loop",
|
||||
@"Connects all edges in a ring around the object.",
|
||||
keyCommandAlt, 'U'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedEdgeCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
int success = 0;
|
||||
UndoUtility.RecordSelection("Insert Edge Loop");
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
Edge[] edges = pb.Connect(ElementSelection.GetEdgeRing(pb, pb.selectedEdges)).item2;
|
||||
|
||||
if (edges != null)
|
||||
{
|
||||
pb.SetSelectedEdges(edges);
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
success++;
|
||||
}
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (success > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3ba8385f1f540519a3524dceb13733b
|
||||
timeCreated: 1457564632
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class MergeFaces : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_Merge";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Merge Faces",
|
||||
@"Tells ProBuilder to treat the selected faces as if they were a single face. Be careful not to use this with unconnected faces!"
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Merge Faces");
|
||||
|
||||
int success = 0;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
if (pb.selectedFaceCount > 1)
|
||||
{
|
||||
success += pb.selectedFaceCount;
|
||||
|
||||
Face face = MergeElements.Merge(pb, pb.selectedFacesInternal);
|
||||
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
|
||||
pb.SetSelectedFaces(new Face[] { face });
|
||||
}
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (success > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Merged " + success + " Faces");
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, "Merge Faces\nNo Faces Selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72b17ff6fd2249038e3d0e0d8969cef0
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.ProBuilder;
|
||||
using ColorUtility = UnityEngine.ProBuilder.ColorUtility;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class OffsetElements : MenuAction
|
||||
{
|
||||
internal enum CoordinateSpace
|
||||
{
|
||||
World,
|
||||
Local,
|
||||
Element,
|
||||
Handle
|
||||
}
|
||||
|
||||
internal static Pref<Vector3> s_Translation = new Pref<Vector3>("MoveElements.s_Translation", Vector3.up);
|
||||
internal static Pref<CoordinateSpace> s_CoordinateSpace = new Pref<CoordinateSpace>("MoveElements.s_CoordinateSpace", CoordinateSpace.World);
|
||||
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
|
||||
public override string iconPath => "Toolbar/OffsetElements";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip => new TooltipContent ( "Offset Elements", "Move the selected elements by a set amount." );
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face | SelectMode.Edge | SelectMode.Vertex; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedVertexCount > 0; }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var dist = s_Translation.value;
|
||||
var coord = s_CoordinateSpace.value;
|
||||
|
||||
var spaceField = new EnumField("Coordinate Space", coord);
|
||||
root.Add(spaceField);
|
||||
spaceField.RegisterCallback<ChangeEvent<Enum>>(evt =>
|
||||
{
|
||||
CoordinateSpace newValue = (CoordinateSpace)evt.newValue;
|
||||
if (s_CoordinateSpace.value == newValue)
|
||||
return;
|
||||
s_CoordinateSpace.SetValue(newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
});
|
||||
|
||||
var distField = new Vector3Field("Translate");
|
||||
distField.SetValueWithoutNotify(dist);
|
||||
distField.Query<FloatField>().ForEach(ff => ff.isDelayed = PreviewActionManager.delayedPreview);
|
||||
root.Add(distField);
|
||||
distField.RegisterCallback<ChangeEvent<Vector3>>(evt =>
|
||||
{
|
||||
s_Translation.SetValue(evt.newValue, true);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
});
|
||||
|
||||
Action onDelayedPreviewChanged = () =>
|
||||
{
|
||||
distField.Query<FloatField>().ForEach(ff => ff.isDelayed = PreviewActionManager.delayedPreview);
|
||||
};
|
||||
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
|
||||
root.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||
{
|
||||
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public override void DoSceneGUI(SceneView sceneView)
|
||||
=> MoveElementsSettings.OnSceneGUI(sceneView);
|
||||
|
||||
protected override void DoAlternateAction()
|
||||
{
|
||||
ConfigurableWindow.GetWindow<MoveElementsSettings>(true, "Offset Settings", true);
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Offset Elements(s)");
|
||||
|
||||
var handleRotation = MeshSelection.GetHandleRotation();
|
||||
|
||||
foreach (var group in MeshSelection.elementSelection)
|
||||
{
|
||||
var mesh = group.mesh;
|
||||
var positions = mesh.positionsInternal;
|
||||
var offset = s_Translation.value;
|
||||
|
||||
switch (s_CoordinateSpace.value)
|
||||
{
|
||||
case CoordinateSpace.World:
|
||||
case CoordinateSpace.Handle:
|
||||
{
|
||||
var pre = mesh.transform.localToWorldMatrix;
|
||||
var post = mesh.transform.worldToLocalMatrix;
|
||||
|
||||
if (s_CoordinateSpace.value == CoordinateSpace.Handle)
|
||||
offset = handleRotation * offset;
|
||||
|
||||
foreach (var index in mesh.selectedCoincidentVertices)
|
||||
{
|
||||
var p = pre.MultiplyPoint3x4(positions[index]);
|
||||
p += offset;
|
||||
positions[index] = post.MultiplyPoint3x4(p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CoordinateSpace.Local:
|
||||
{
|
||||
foreach (var index in mesh.selectedCoincidentVertices)
|
||||
positions[index] += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case CoordinateSpace.Element:
|
||||
{
|
||||
foreach (var elements in group.elementGroups)
|
||||
{
|
||||
var rotation = Quaternion.Inverse(mesh.transform.rotation) * elements.rotation;
|
||||
var o = rotation * offset;
|
||||
foreach (var index in elements.indices)
|
||||
positions[index] += o;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.Rebuild();
|
||||
mesh.Optimize();
|
||||
ProBuilderEditor.Refresh();
|
||||
}
|
||||
|
||||
if(ProBuilderEditor.selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
|
||||
return new ActionResult(ActionResult.Status.Success, "Move " + MeshSelection.selectedEdgeCount + (MeshSelection.selectedEdgeCount > 1 ? " Edges" : " Edge"));
|
||||
if(ProBuilderEditor.selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace))
|
||||
return new ActionResult(ActionResult.Status.Success, "Move " + MeshSelection.selectedFaceCount + (MeshSelection.selectedFaceCount > 1 ? " Faces" : " Face"));
|
||||
return new ActionResult(ActionResult.Status.Success, "Move " + MeshSelection.selectedVertexCount + (MeshSelection.selectedVertexCount > 1 ? " Vertices" : " Vertex"));
|
||||
}
|
||||
}
|
||||
|
||||
class MoveElementsSettings : ConfigurableWindow
|
||||
{
|
||||
void OnEnable()
|
||||
{
|
||||
titleContent.text = L10n.Tr("Offset Element Settings");
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
DoContextMenu();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var dist = OffsetElements.s_Translation.value;
|
||||
var coord = OffsetElements.s_CoordinateSpace.value;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
coord = (OffsetElements.CoordinateSpace) EditorGUILayout.EnumPopup("Coordinate Space", coord);
|
||||
dist = EditorGUILayout.Vector3Field("Translate", dist);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
OffsetElements.s_Translation.SetValue(dist, true);
|
||||
OffsetElements.s_CoordinateSpace.SetValue(coord);
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button(L10n.Tr("Offset Selection")))
|
||||
{
|
||||
var instance = EditorToolbarLoader.GetInstance<OffsetElements>();
|
||||
EditorUtility.ShowNotification(instance.PerformAction().notification);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
static List<Vector3> s_Points = new List<Vector3>();
|
||||
|
||||
[InitializeOnEnterPlayMode]
|
||||
static void ResetStaticsOnLoad()
|
||||
{
|
||||
s_Points.Clear();
|
||||
}
|
||||
|
||||
internal static void OnSceneGUI(SceneView view)
|
||||
{
|
||||
s_Points.Clear();
|
||||
|
||||
var coord = OffsetElements.s_CoordinateSpace.value;
|
||||
var offset = OffsetElements.s_Translation.value;
|
||||
var handleRotation = MeshSelection.GetHandleRotation();
|
||||
var camera = view.camera.transform.forward * -.01f;
|
||||
|
||||
foreach (var selection in MeshSelection.elementSelection)
|
||||
{
|
||||
var mesh = selection.mesh;
|
||||
|
||||
if (coord == OffsetElements.CoordinateSpace.Element)
|
||||
{
|
||||
foreach (var elements in selection.elementGroups)
|
||||
{
|
||||
s_Points.Add(elements.position + camera);
|
||||
s_Points.Add(elements.rotation * offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var preview = offset;
|
||||
|
||||
if (coord == OffsetElements.CoordinateSpace.Handle)
|
||||
preview = handleRotation * offset;
|
||||
else if (coord == OffsetElements.CoordinateSpace.Local)
|
||||
preview = mesh.transform.TransformDirection(offset);
|
||||
|
||||
foreach (var elements in selection.elementGroups)
|
||||
{
|
||||
s_Points.Add(elements.position + camera);
|
||||
s_Points.Add(preview);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (var lines = new EditorHandleDrawing.LineDrawingScope(ColorUtility.GetColor(offset)))
|
||||
{
|
||||
for (int i = 0; i < s_Points.Count; i += 2)
|
||||
lines.DrawLine(s_Points[i], s_Points[i] + s_Points[i + 1]);
|
||||
}
|
||||
|
||||
using (var points = new EditorHandleDrawing.PointDrawingScope(Color.gray))
|
||||
{
|
||||
for (int i = 0; i < s_Points.Count; i += 2)
|
||||
points.Draw(s_Points[i]);
|
||||
}
|
||||
|
||||
using(var points = new EditorHandleDrawing.PointDrawingScope(ColorUtility.GetColor(offset)))
|
||||
{
|
||||
for (int i = 0; i < s_Points.Count; i += 2)
|
||||
points.Draw(s_Points[i] + s_Points[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fb3641deade84f238d59ad03971b64c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class SetPivotToSelection : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => "Toolbar/Pivot_CenterOnElements";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
public override TooltipContent tooltip { get { return _tooltip; } }
|
||||
public override string menuTitle { get { return "Set Pivot To Selection"; } }
|
||||
|
||||
static readonly TooltipContent _tooltip = new TooltipContent
|
||||
(
|
||||
"Set Pivot to Center of Selection",
|
||||
@"Moves the pivot point of each mesh to the average of all selected elements positions. This means the pivot point moves to where-ever the handle currently is."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.enabled && (MeshSelection.selectedVertexCount > 0
|
||||
|| MeshSelection.selectedEdgeCount > 0
|
||||
|| MeshSelection.selectedFaceCount > 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
Object[] objects = new Object[MeshSelection.selectedObjectCount * 2];
|
||||
|
||||
for (int i = 0, c = MeshSelection.selectedObjectCount; i < c; i++)
|
||||
{
|
||||
objects[i] = MeshSelection.topInternal[i];
|
||||
objects[i + c] = MeshSelection.topInternal[i].transform;
|
||||
}
|
||||
|
||||
UndoUtility.RegisterCompleteObjectUndo(objects, "Set Pivot");
|
||||
|
||||
foreach (var mesh in MeshSelection.topInternal)
|
||||
{
|
||||
TransformUtility.UnparentChildren(mesh.transform);
|
||||
mesh.CenterPivot(mesh.selectedIndexesInternal);
|
||||
mesh.Optimize();
|
||||
TransformUtility.ReparentChildren(mesh.transform);
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return new ActionResult(ActionResult.Status.Success, "Set Pivot");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6bc50937fe34666940ca23931be5344
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ProBuilder.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEditor.ProBuilder;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class SmartConnect : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
|
||||
public override string iconPath => string.Empty;
|
||||
public override Texture2D icon => null;
|
||||
public override TooltipContent tooltip { get { return _tooltip; } }
|
||||
|
||||
static readonly TooltipContent _tooltip = new TooltipContent
|
||||
(
|
||||
"Smart Connect",
|
||||
"",
|
||||
keyCommandAlt, 'E'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.enabled && (MeshSelection.selectedVertexCount > 1
|
||||
|| MeshSelection.selectedEdgeCount > 1
|
||||
|| MeshSelection.selectedFaceCount > 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool hidden
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
switch (ProBuilderEditor.selectMode)
|
||||
{
|
||||
case SelectMode.Vertex:
|
||||
return EditorToolbarLoader.GetInstance<ConnectVertices>().PerformAction();
|
||||
|
||||
default:
|
||||
return EditorToolbarLoader.GetInstance<ConnectEdges>().PerformAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e99f90a32784f4d9ff62732e1265f23
|
||||
timeCreated: 1467037553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ProBuilder.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEditor.ProBuilder;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class SmartSubdivide : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
|
||||
public override string iconPath => string.Empty;
|
||||
public override Texture2D icon => null;
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Smart Subdivide",
|
||||
"",
|
||||
keyCommandAlt, 'S'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge | SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && (MeshSelection.selectedEdgeCount > 0 || MeshSelection.selectedFaceCount > 0); }
|
||||
}
|
||||
|
||||
public override bool hidden
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
switch (ProBuilderEditor.selectMode)
|
||||
{
|
||||
case SelectMode.Edge:
|
||||
return EditorToolbarLoader.GetInstance<SubdivideEdges>().PerformAction();
|
||||
|
||||
default:
|
||||
return EditorToolbarLoader.GetInstance<SubdivideFaces>().PerformAction();
|
||||
}
|
||||
}
|
||||
|
||||
internal override string GetMenuItemOverride()
|
||||
{
|
||||
return @" switch (ProBuilderEditor.selectMode)
|
||||
{
|
||||
case SelectMode.Edge:
|
||||
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<SubdivideEdges>(), true));
|
||||
break;
|
||||
default:
|
||||
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<SubdivideFaces>(), true));
|
||||
break;
|
||||
}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da5baec4bf414efb91d2253bef40dd19
|
||||
timeCreated: 1469818738
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class SplitVertices : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Vert_Split";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Split Vertices",
|
||||
@"Disconnects vertices that share the same position in space so that they may be moved independently of one another.",
|
||||
keyCommandAlt, 'X'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Vertex; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
// This isn't completely accurate, because to check that each selected vertex has coincident vertices is
|
||||
// unnecessarily expensive for the purposes of this property. So here we handle the most common case,
|
||||
// where a single vertex is selected with no additional coincident vertices.
|
||||
return base.enabled
|
||||
&& MeshSelection.selectedVertexCountObjectMax > 0
|
||||
&& !(MeshSelection.selectedVertexCountObjectMax == 1 && MeshSelection.selectedCoincidentVertexCountMax == 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
int splitCount = 0;
|
||||
UndoUtility.RecordSelection("Split Vertices");
|
||||
|
||||
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
||||
{
|
||||
var coincident = mesh.selectedCoincidentVertices;
|
||||
splitCount += mesh.selectedSharedVerticesCount;
|
||||
mesh.SplitVertices(coincident);
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (splitCount > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Split " + splitCount + (splitCount > 1 ? " Vertices" : " Vertex"));
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, "Split Vertices\nInsuffient Vertices Selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c339ea7067e6fcd459cb4eac2f0dafb1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class SubdivideEdges : MenuAction
|
||||
{
|
||||
Pref<int> m_SubdivisionCount = new Pref<int>("SubdivideEdges.subdivisions", 1);
|
||||
Pref<int> m_SubdivisionUIMin = new Pref<int>("SubdivideEdges.subdivisionsUIMin", 1);
|
||||
Pref<int> m_SubdivisionUIMax = new Pref<int>("SubdivideEdges.subdivisionsUIMax", 32);
|
||||
Pref<bool> m_SubdivisionRangeExpanded = new Pref<bool>("SubdivideEdges.rangeExpanded", false);
|
||||
const int m_SubdivisionMin = 1;
|
||||
const int m_SubdivisionMax = 512;
|
||||
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Edge_Subdivide";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
protected internal override bool hasFileMenuEntry
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Subdivide Edges",
|
||||
"Appends evenly spaced new vertices to the selected edges.",
|
||||
keyCommandAlt, 'S'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Edge; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedEdgeCount > 0; }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
Vector2IntField m_RangeField;
|
||||
IntegerField m_SubdivCount;
|
||||
SliderInt m_Slider;
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var line = new VisualElement();
|
||||
line.style.flexDirection = FlexDirection.Row;
|
||||
var tooltip = "How many vertices to insert on each selected edge. Vertices will be equally spaced between " +
|
||||
"one another and the boundaries of the edge.";
|
||||
|
||||
var foldout = new Foldout();
|
||||
foldout.SetValueWithoutNotify(m_SubdivisionRangeExpanded.value);
|
||||
foldout.RegisterCallback<ChangeEvent<bool>>(OnFoldoutChanged);
|
||||
m_Slider = new SliderInt("Subdivisions", m_SubdivisionUIMin, m_SubdivisionUIMax);
|
||||
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
m_Slider.tooltip = tooltip;
|
||||
m_Slider.RegisterCallback<ChangeEvent<int>>(OnSliderChanged);
|
||||
m_Slider.style.flexGrow = 1f;
|
||||
m_SubdivCount = new IntegerField();
|
||||
m_SubdivCount.isDelayed = PreviewActionManager.delayedPreview;
|
||||
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
m_SubdivCount.tooltip = tooltip;
|
||||
m_SubdivCount.RegisterCallback<ChangeEvent<int>>(OnCountChanged);
|
||||
m_SubdivCount.style.width = 40;
|
||||
Action onDelayedPreviewChanged = () =>
|
||||
{
|
||||
m_SubdivCount.isDelayed = PreviewActionManager.delayedPreview;
|
||||
};
|
||||
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
|
||||
root.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||
{
|
||||
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
|
||||
});
|
||||
line.Add(foldout);
|
||||
line.Add(m_Slider);
|
||||
line.Add(m_SubdivCount);
|
||||
root.Add(line);
|
||||
|
||||
m_RangeField = new Vector2IntField("Range");
|
||||
m_RangeField.style.paddingLeft = new StyleLength(25);
|
||||
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
|
||||
m_RangeField.Q<Label>().style.flexGrow = 1;
|
||||
|
||||
var label = m_RangeField.Q<IntegerField>("unity-x-input").Q<Label>();
|
||||
label.text = String.Empty;
|
||||
label.style.flexGrow = 0;
|
||||
label = m_RangeField.Q<IntegerField>("unity-y-input").Q<Label>();
|
||||
label.text = String.Empty;
|
||||
label.style.flexGrow = 0;
|
||||
|
||||
var field = m_RangeField.Q<IntegerField>("unity-x-input");
|
||||
field.isDelayed = true;
|
||||
field.RegisterCallback<ChangeEvent<int>>(OnMinChanged);
|
||||
field.style.width = 40f;
|
||||
var child = field[0];
|
||||
child.style.display = DisplayStyle.None;
|
||||
child = m_RangeField.Q<IntegerField>("unity-x-input")[1];
|
||||
child.style.flexShrink = 0;
|
||||
|
||||
field = m_RangeField.Q<IntegerField>("unity-y-input");
|
||||
field.isDelayed = true;
|
||||
field.RegisterCallback<ChangeEvent<int>>(OnMaxChanged);
|
||||
field.style.width = 40f;
|
||||
child = field[0];
|
||||
child.style.display = DisplayStyle.None;
|
||||
child = m_RangeField.Q<IntegerField>("unity-y-input")[1];
|
||||
child.style.flexShrink = 0;
|
||||
|
||||
child = m_RangeField[1][2];
|
||||
child.style.display = DisplayStyle.None;
|
||||
|
||||
m_RangeField.style.display = m_SubdivisionRangeExpanded.value ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
root.Add(m_RangeField);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void OnFoldoutChanged(ChangeEvent<bool> evt)
|
||||
{
|
||||
if(m_SubdivisionRangeExpanded.value == evt.newValue)
|
||||
return;
|
||||
|
||||
m_SubdivisionRangeExpanded.SetValue(evt.newValue);
|
||||
m_RangeField.style.display = m_SubdivisionRangeExpanded.value ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
void OnSliderChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
if(m_SubdivisionCount.value == evt.newValue)
|
||||
return;
|
||||
|
||||
m_SubdivisionCount.SetValue(evt.newValue);
|
||||
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
|
||||
void OnCountChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
if(m_SubdivisionCount.value == evt.newValue)
|
||||
return;
|
||||
|
||||
m_SubdivisionCount.SetValue(evt.newValue < 1 ? 1 : evt.newValue);
|
||||
if (m_SubdivisionCount.value < m_SubdivisionUIMin.value)
|
||||
{
|
||||
m_SubdivisionUIMin.SetValue(m_SubdivisionCount.value);
|
||||
m_Slider.lowValue = m_SubdivisionUIMin.value;
|
||||
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
|
||||
}
|
||||
if (m_SubdivisionCount.value > m_SubdivisionUIMax.value)
|
||||
{
|
||||
m_SubdivisionUIMax.SetValue(m_SubdivisionCount.value);
|
||||
m_Slider.highValue = m_SubdivisionUIMax.value;
|
||||
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
|
||||
}
|
||||
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
|
||||
void OnMinChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
if(m_SubdivisionUIMin.value == evt.newValue)
|
||||
return;
|
||||
|
||||
m_SubdivisionUIMin.SetValue(evt.newValue < 1 ? 1 : evt.newValue);
|
||||
m_Slider.lowValue = m_SubdivisionUIMin.value;
|
||||
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
|
||||
if (m_SubdivisionCount.value < m_SubdivisionUIMin.value)
|
||||
{
|
||||
m_SubdivisionCount.SetValue(m_SubdivisionUIMin.value);
|
||||
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
void OnMaxChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
if(m_SubdivisionUIMax.value == evt.newValue)
|
||||
return;
|
||||
|
||||
m_SubdivisionUIMax.SetValue(evt.newValue);
|
||||
m_Slider.highValue = m_SubdivisionUIMax.value;
|
||||
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
|
||||
if (m_SubdivisionCount.value > m_SubdivisionUIMax.value)
|
||||
{
|
||||
m_SubdivisionCount.SetValue(m_SubdivisionUIMax.value);
|
||||
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Subdivide Edge Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.HelpBox("How many vertices to insert on each selected edge.\n\nVertices will be equally spaced between one another and the boundaries of the edge.", MessageType.Info);
|
||||
int minUIRange = m_SubdivisionUIMin.value;
|
||||
int maxUIRange = m_SubdivisionUIMax.value;
|
||||
bool expanded = m_SubdivisionRangeExpanded.value;
|
||||
m_SubdivisionCount.value = (int)UI.EditorGUIUtility.FreeSliderWithRange("Subdivisions", (int)m_SubdivisionCount.value, m_SubdivisionMin, m_SubdivisionMax, ref minUIRange, ref maxUIRange, ref expanded);
|
||||
m_SubdivisionUIMin.value = minUIRange;
|
||||
m_SubdivisionUIMax.value = maxUIRange;
|
||||
m_SubdivisionRangeExpanded.value = expanded;
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
ProBuilderSettings.Save();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Subdivide Edges"))
|
||||
PerformAction();
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
int subdivisions = m_SubdivisionCount;
|
||||
|
||||
UndoUtility.RecordSelection("Subdivide Edges");
|
||||
|
||||
ActionResult result = ActionResult.NoSelection;
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
List<Edge> newEdgeSelection = AppendElements.AppendVerticesToEdge(pb, pb.selectedEdges, subdivisions);
|
||||
|
||||
if (newEdgeSelection != null)
|
||||
{
|
||||
pb.SetSelectedEdges(newEdgeSelection);
|
||||
pb.ToMesh();
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
result = new ActionResult(ActionResult.Status.Success, "Subdivide Edge");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new ActionResult(ActionResult.Status.Failure, "Failed Subdivide Edge");
|
||||
}
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48a7fe196a624a7bae17c8621899b11c
|
||||
timeCreated: 1459347828
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class SubdivideFaces : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_Subdivide";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
protected internal override bool hasFileMenuEntry
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Subdivide Faces",
|
||||
@"Inserts a new vertex at the center of each selected face and creates a new edge from the center of each perimeter edge to the center vertex.",
|
||||
keyCommandAlt, 'S'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
int success = 0;
|
||||
UndoUtility.RecordSelection("Subdivide Faces");
|
||||
|
||||
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
|
||||
{
|
||||
Face[] faces = pb.Subdivide(pb.selectedFacesInternal);
|
||||
|
||||
pb.ToMesh();
|
||||
|
||||
if (faces != null)
|
||||
{
|
||||
success += pb.selectedFacesInternal.Length;
|
||||
pb.SetSelectedFaces(faces);
|
||||
|
||||
pb.Refresh();
|
||||
pb.Optimize();
|
||||
}
|
||||
}
|
||||
|
||||
if (success > 0)
|
||||
{
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return new ActionResult(ActionResult.Status.Success, "Subdivide " + success + ((success > 1) ? " faces" : " face"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Subdivide faces failed - did you not have any faces selected?");
|
||||
return new ActionResult(ActionResult.Status.Failure, "Subdivide Faces\nNo faces selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdc593ceb4be46bfab31b9c445ec89a2
|
||||
timeCreated: 1457564631
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class TriangulateFaces : MenuAction
|
||||
{
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Face_Triangulate";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Triangulate Faces",
|
||||
"Break all selected faces down to triangles."
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Face; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
ActionResult res = ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Triangulate Faces");
|
||||
|
||||
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
||||
{
|
||||
mesh.ToMesh();
|
||||
Face[] triangulatedFaces = mesh.ToTriangles(mesh.selectedFacesInternal);
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
mesh.SetSelectedFaces(triangulatedFaces);
|
||||
res = new ActionResult(ActionResult.Status.Success, string.Format("Triangulated {0} {1}", triangulatedFaces.Length, triangulatedFaces.Length < 2 ? "Face" : "Faces"));
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 729d0f7265af462a8b41492704404f59
|
||||
timeCreated: 1470747992
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using UnityEngine.ProBuilder.MeshOperations;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ProBuilder.Actions
|
||||
{
|
||||
sealed class WeldVertices : MenuAction
|
||||
{
|
||||
Pref<float> m_WeldDistance = new Pref<float>("WeldVertices.weldDistance", .01f);
|
||||
static readonly GUIContent gc_weldDistance = new GUIContent("Weld Distance", "The maximum distance between two vertices in order to be welded together.");
|
||||
const float k_MinWeldDistance = .00001f;
|
||||
|
||||
public override ToolbarGroup group
|
||||
{
|
||||
get { return ToolbarGroup.Geometry; }
|
||||
}
|
||||
|
||||
public override string iconPath => "Toolbar/Vert_Weld";
|
||||
public override Texture2D icon => IconUtility.GetIcon(iconPath);
|
||||
|
||||
public override TooltipContent tooltip
|
||||
{
|
||||
get { return s_Tooltip; }
|
||||
}
|
||||
|
||||
static readonly TooltipContent s_Tooltip = new TooltipContent
|
||||
(
|
||||
"Weld Vertices",
|
||||
@"Searches the current selection for vertices that are within the specified distance of on another and merges them into a single vertex.",
|
||||
keyCommandAlt, 'V'
|
||||
);
|
||||
|
||||
public override SelectMode validSelectModes
|
||||
{
|
||||
get { return SelectMode.Vertex; }
|
||||
}
|
||||
|
||||
public override bool enabled
|
||||
{
|
||||
get { return base.enabled && MeshSelection.selectedSharedVertexCountObjectMax > 1; }
|
||||
}
|
||||
|
||||
protected override MenuActionState optionsMenuState
|
||||
{
|
||||
get { return MenuActionState.VisibleAndEnabled; }
|
||||
}
|
||||
|
||||
public override VisualElement CreateSettingsContent()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
root.style.minWidth = 150;
|
||||
|
||||
var floatField = new FloatField(gc_weldDistance.text);
|
||||
floatField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
floatField.tooltip = gc_weldDistance.tooltip;
|
||||
floatField.SetValueWithoutNotify(m_WeldDistance);
|
||||
floatField.Q("unity-text-input").style.minWidth = 50;
|
||||
floatField.RegisterCallback<ChangeEvent<float>>(evt =>
|
||||
{
|
||||
if (evt.newValue < k_MinWeldDistance)
|
||||
{
|
||||
m_WeldDistance.SetValue(k_MinWeldDistance);
|
||||
floatField.SetValueWithoutNotify(m_WeldDistance);
|
||||
}
|
||||
else
|
||||
m_WeldDistance.SetValue(evt.newValue);
|
||||
PreviewActionManager.UpdatePreview();
|
||||
});
|
||||
Action onDelayedPreviewChanged = () =>
|
||||
{
|
||||
floatField.isDelayed = PreviewActionManager.delayedPreview;
|
||||
};
|
||||
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
|
||||
root.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||
{
|
||||
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
|
||||
});
|
||||
root.Add(floatField);
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void OnSettingsGUI()
|
||||
{
|
||||
GUILayout.Label("Weld Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_WeldDistance.value = EditorGUILayout.FloatField(gc_weldDistance, m_WeldDistance);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (m_WeldDistance < k_MinWeldDistance)
|
||||
m_WeldDistance.value = k_MinWeldDistance;
|
||||
ProBuilderSettings.Save();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Weld Vertices"))
|
||||
PerformAction();
|
||||
}
|
||||
|
||||
protected override ActionResult PerformActionImplementation()
|
||||
{
|
||||
if (MeshSelection.selectedObjectCount < 1)
|
||||
return ActionResult.NoSelection;
|
||||
|
||||
ActionResult res = ActionResult.NoSelection;
|
||||
|
||||
UndoUtility.RecordSelection("Weld Vertices");
|
||||
|
||||
int weldCount = 0;
|
||||
|
||||
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
|
||||
{
|
||||
weldCount += mesh.sharedVerticesInternal.Length;
|
||||
|
||||
if (mesh.selectedIndexesInternal.Length > 1)
|
||||
{
|
||||
mesh.ToMesh();
|
||||
|
||||
var selectedVertices = mesh.GetCoincidentVertices(mesh.selectedVertices);
|
||||
int[] welds = mesh.WeldVertices(mesh.selectedIndexesInternal, m_WeldDistance);
|
||||
res = welds != null ? new ActionResult(ActionResult.Status.Success, "Weld Vertices") : new ActionResult(ActionResult.Status.Failure, "Failed Weld Vertices");
|
||||
|
||||
if (res)
|
||||
{
|
||||
var newSelection = welds ?? new int[0] { };
|
||||
|
||||
if (MeshValidation.ContainsDegenerateTriangles(mesh))
|
||||
{
|
||||
List<int> removedIndices = new List<int>();
|
||||
var vertexCount = mesh.vertexCount;
|
||||
|
||||
if(MeshValidation.RemoveDegenerateTriangles(mesh, removedIndices))
|
||||
{
|
||||
if (removedIndices.Count < vertexCount)
|
||||
{
|
||||
var newlySelectedVertices = new List<int>();
|
||||
selectedVertices.Sort();
|
||||
removedIndices.Sort();
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < selectedVertices.Count; i++)
|
||||
{
|
||||
if (count >= removedIndices.Count || selectedVertices[i] != removedIndices[count])
|
||||
{
|
||||
newlySelectedVertices.Add(selectedVertices[i] - UnityEngine.ProBuilder.ArrayUtility.NearestIndexPriorToValue(removedIndices, selectedVertices[i]) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
newSelection = newlySelectedVertices.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
newSelection = new int[0];
|
||||
}
|
||||
}
|
||||
mesh.ToMesh();
|
||||
}
|
||||
mesh.SetSelectedVertices(newSelection);
|
||||
}
|
||||
|
||||
mesh.Refresh();
|
||||
mesh.Optimize();
|
||||
}
|
||||
|
||||
weldCount -= mesh.sharedVerticesInternal.Length;
|
||||
}
|
||||
|
||||
ProBuilderEditor.Refresh();
|
||||
|
||||
if (res && weldCount > 0)
|
||||
return new ActionResult(ActionResult.Status.Success, "Weld " + weldCount + (weldCount > 1 ? " Vertices" : " Vertex"));
|
||||
|
||||
return new ActionResult(ActionResult.Status.Failure, "Nothing to Weld");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a62bb434134faa6439b1998669525768
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user