Files

273 lines
10 KiB
C#

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]);
}
}
}
}