540 lines
17 KiB
C#
540 lines
17 KiB
C#
|
using UnityEngine;
|
||
|
|
||
|
namespace UnityEditor.U2D.Animation
|
||
|
{
|
||
|
internal class SpriteMeshView : ISpriteMeshView
|
||
|
{
|
||
|
readonly int m_VertexHashCode = "Vertex".GetHashCode();
|
||
|
readonly int m_EdgeHashCode = "Edge".GetHashCode();
|
||
|
const string kDeleteCommandName = "Delete";
|
||
|
const string kSoftDeleteCommandName = "SoftDelete";
|
||
|
static readonly Color kEdgeColor = Color.cyan;
|
||
|
static readonly Color kEdgeHoveredColor = Color.yellow;
|
||
|
static readonly Color kEdgeSelectedColor = Color.yellow;
|
||
|
const float kEdgeWidth = 2f;
|
||
|
const float kVertexRadius = 2.5f;
|
||
|
|
||
|
private class Styles
|
||
|
{
|
||
|
public readonly GUIStyle pointNormalStyle;
|
||
|
public readonly GUIStyle pointHoveredStyle;
|
||
|
public readonly GUIStyle pointSelectedStyle;
|
||
|
|
||
|
public Styles()
|
||
|
{
|
||
|
Texture2D pointNormal = ResourceLoader.Load<Texture2D>("SkinningModule/dotCyan.png");
|
||
|
Texture2D pointHovered = ResourceLoader.Load<Texture2D>("SkinningModule/dotYellow.png");
|
||
|
Texture2D pointSelected = ResourceLoader.Load<Texture2D>("SkinningModule/dotYellow.png");
|
||
|
|
||
|
pointNormalStyle = new GUIStyle();
|
||
|
pointNormalStyle.normal.background = pointNormal;
|
||
|
pointNormalStyle.fixedWidth = 8f;
|
||
|
pointNormalStyle.fixedHeight = 8f;
|
||
|
|
||
|
pointHoveredStyle = new GUIStyle();
|
||
|
pointHoveredStyle.normal.background = pointHovered;
|
||
|
pointHoveredStyle.fixedWidth = 10f;
|
||
|
pointHoveredStyle.fixedHeight = 10f;
|
||
|
|
||
|
pointSelectedStyle = new GUIStyle();
|
||
|
pointSelectedStyle.normal.background = pointSelected;
|
||
|
pointSelectedStyle.fixedWidth = 10f;
|
||
|
pointSelectedStyle.fixedHeight = 10f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Styles m_Styles;
|
||
|
private Styles styles
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (m_Styles == null)
|
||
|
m_Styles = new Styles();
|
||
|
|
||
|
return m_Styles;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int m_HoveredEdge = -1;
|
||
|
int m_HoveredEdgeControlID = -1;
|
||
|
int m_MoveEdgeControlID = -1;
|
||
|
int m_HoveredVertex = -1;
|
||
|
int m_PrevHoveredVertex = -1;
|
||
|
int m_HoveredVertexControlID = -1;
|
||
|
int m_MoveVertexControlID = -1;
|
||
|
Color m_TempColor;
|
||
|
SliderData m_HotSliderData = SliderData.zero;
|
||
|
MeshEditorAction m_PreviousActiveAction = MeshEditorAction.None;
|
||
|
private Vector2 m_MouseWorldPosition;
|
||
|
private float m_NearestVertexDistance;
|
||
|
private float m_NearestEdgeDistance;
|
||
|
private int m_NearestVertex = -1;
|
||
|
private int m_NearestEdge = -1;
|
||
|
|
||
|
|
||
|
public SpriteMeshViewMode mode { get; set; }
|
||
|
public ISelection<int> selection { get; set; }
|
||
|
public int defaultControlID { get; set; }
|
||
|
public Rect frame { get; set; }
|
||
|
private IGUIWrapper guiWrapper { get; set; }
|
||
|
|
||
|
public Vector2 mouseWorldPosition
|
||
|
{
|
||
|
get { return m_MouseWorldPosition; }
|
||
|
}
|
||
|
|
||
|
public int hoveredVertex
|
||
|
{
|
||
|
get { return m_HoveredVertex; }
|
||
|
}
|
||
|
|
||
|
public int hoveredEdge
|
||
|
{
|
||
|
get { return m_HoveredEdge; }
|
||
|
}
|
||
|
|
||
|
public int closestEdge
|
||
|
{
|
||
|
get { return m_NearestEdge; }
|
||
|
}
|
||
|
|
||
|
public SpriteMeshView(IGUIWrapper gw)
|
||
|
{
|
||
|
guiWrapper = gw;
|
||
|
}
|
||
|
|
||
|
public void CancelMode()
|
||
|
{
|
||
|
if (mode != SpriteMeshViewMode.EditGeometry)
|
||
|
{
|
||
|
if (guiWrapper.IsKeyDown(KeyCode.Escape) || guiWrapper.IsMouseDown(1))
|
||
|
{
|
||
|
mode = SpriteMeshViewMode.EditGeometry;
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void BeginLayout()
|
||
|
{
|
||
|
var vertexControlID = guiWrapper.GetControlID(m_VertexHashCode, FocusType.Passive);
|
||
|
var edgeControlID = guiWrapper.GetControlID(m_EdgeHashCode, FocusType.Passive);
|
||
|
|
||
|
if (guiWrapper.eventType == EventType.Layout || guiWrapper.eventType == EventType.MouseMove)
|
||
|
{
|
||
|
m_NearestVertexDistance = float.MaxValue;
|
||
|
m_NearestEdgeDistance = float.MaxValue;
|
||
|
m_NearestVertex = -1;
|
||
|
m_NearestEdge = -1;
|
||
|
m_MouseWorldPosition = guiWrapper.GUIToWorld(guiWrapper.mousePosition);
|
||
|
m_HoveredVertexControlID = vertexControlID;
|
||
|
m_HoveredEdgeControlID = edgeControlID;
|
||
|
m_PrevHoveredVertex = m_HoveredVertex;
|
||
|
m_HoveredVertex = -1;
|
||
|
m_HoveredEdge = -1;
|
||
|
|
||
|
if (guiWrapper.IsControlHot(0))
|
||
|
{
|
||
|
m_MoveVertexControlID = -1;
|
||
|
m_MoveEdgeControlID = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void EndLayout()
|
||
|
{
|
||
|
guiWrapper.LayoutControl(m_HoveredEdgeControlID, m_NearestEdgeDistance);
|
||
|
guiWrapper.LayoutControl(m_HoveredVertexControlID, m_NearestVertexDistance);
|
||
|
|
||
|
if(guiWrapper.IsControlNearest(m_HoveredVertexControlID))
|
||
|
m_HoveredVertex = m_NearestVertex;
|
||
|
|
||
|
if (guiWrapper.IsControlNearest(m_HoveredEdgeControlID))
|
||
|
m_HoveredEdge = m_NearestEdge;
|
||
|
|
||
|
if (guiWrapper.eventType == EventType.Layout || guiWrapper.eventType == EventType.MouseMove)
|
||
|
if (m_PrevHoveredVertex != m_HoveredVertex)
|
||
|
guiWrapper.Repaint();
|
||
|
}
|
||
|
|
||
|
public void LayoutVertex(Vector2 position, int index)
|
||
|
{
|
||
|
if (guiWrapper.eventType == EventType.Layout)
|
||
|
{
|
||
|
var distance = guiWrapper.DistanceToCircle(position, kVertexRadius);
|
||
|
|
||
|
if (distance <= m_NearestVertexDistance)
|
||
|
{
|
||
|
m_NearestVertexDistance = distance;
|
||
|
m_NearestVertex = index;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void LayoutEdge(Vector2 startPosition, Vector2 endPosition, int index)
|
||
|
{
|
||
|
if (guiWrapper.eventType == EventType.Layout)
|
||
|
{
|
||
|
var distance = guiWrapper.DistanceToSegment(startPosition, endPosition);
|
||
|
|
||
|
if (distance < m_NearestEdgeDistance)
|
||
|
{
|
||
|
m_NearestEdgeDistance = distance;
|
||
|
m_NearestEdge = index;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool DoCreateVertex()
|
||
|
{
|
||
|
if (mode == SpriteMeshViewMode.CreateVertex && IsActionActive(MeshEditorAction.CreateVertex))
|
||
|
ConsumeMouseMoveEvents();
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.CreateVertex))
|
||
|
{
|
||
|
guiWrapper.SetGuiChanged(true);
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoSelectVertex(out bool additive)
|
||
|
{
|
||
|
additive = false;
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.SelectVertex))
|
||
|
{
|
||
|
additive = guiWrapper.isActionKeyDown;
|
||
|
guiWrapper.Repaint();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoMoveVertex(out Vector2 delta)
|
||
|
{
|
||
|
delta = Vector2.zero;
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.MoveVertex))
|
||
|
{
|
||
|
m_MoveVertexControlID = m_HoveredVertexControlID;
|
||
|
m_HotSliderData.position = mouseWorldPosition;
|
||
|
}
|
||
|
|
||
|
Vector3 newPosition;
|
||
|
if (guiWrapper.DoSlider(m_MoveVertexControlID, m_HotSliderData, out newPosition))
|
||
|
{
|
||
|
delta = newPosition - m_HotSliderData.position;
|
||
|
m_HotSliderData.position = newPosition;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoMoveEdge(out Vector2 delta)
|
||
|
{
|
||
|
delta = Vector2.zero;
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.MoveEdge))
|
||
|
{
|
||
|
m_MoveEdgeControlID = m_HoveredEdgeControlID;
|
||
|
m_HotSliderData.position = mouseWorldPosition;
|
||
|
}
|
||
|
|
||
|
Vector3 newPosition;
|
||
|
if (guiWrapper.DoSlider(m_MoveEdgeControlID, m_HotSliderData, out newPosition))
|
||
|
{
|
||
|
delta = newPosition - m_HotSliderData.position;
|
||
|
m_HotSliderData.position = newPosition;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoCreateEdge()
|
||
|
{
|
||
|
if (IsActionActive(MeshEditorAction.CreateEdge))
|
||
|
ConsumeMouseMoveEvents();
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.CreateEdge))
|
||
|
{
|
||
|
guiWrapper.SetGuiChanged(true);
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoSplitEdge()
|
||
|
{
|
||
|
if (IsActionActive(MeshEditorAction.SplitEdge))
|
||
|
ConsumeMouseMoveEvents();
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.SplitEdge))
|
||
|
{
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
guiWrapper.SetGuiChanged(true);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoSelectEdge(out bool additive)
|
||
|
{
|
||
|
additive = false;
|
||
|
|
||
|
if (IsActionTriggered(MeshEditorAction.SelectEdge))
|
||
|
{
|
||
|
additive = guiWrapper.isActionKeyDown;
|
||
|
guiWrapper.Repaint();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool DoRemove()
|
||
|
{
|
||
|
if (IsActionTriggered(MeshEditorAction.Remove))
|
||
|
{
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
guiWrapper.SetGuiChanged(true);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public void DrawVertex(Vector2 position)
|
||
|
{
|
||
|
DrawingUtility.DrawGUIStyleCap(0, position, Quaternion.identity, 1f, styles.pointNormalStyle);
|
||
|
}
|
||
|
|
||
|
public void DrawVertexHovered(Vector2 position)
|
||
|
{
|
||
|
DrawingUtility.DrawGUIStyleCap(0, position, Quaternion.identity, 1f, styles.pointHoveredStyle);
|
||
|
}
|
||
|
|
||
|
public void DrawVertexSelected(Vector2 position)
|
||
|
{
|
||
|
DrawingUtility.DrawGUIStyleCap(0, position, Quaternion.identity, 1f, styles.pointSelectedStyle);
|
||
|
}
|
||
|
|
||
|
public void BeginDrawEdges()
|
||
|
{
|
||
|
if (guiWrapper.eventType != EventType.Repaint)
|
||
|
return;
|
||
|
|
||
|
DrawingUtility.BeginSolidLines();
|
||
|
m_TempColor = Handles.color;
|
||
|
}
|
||
|
|
||
|
public void EndDrawEdges()
|
||
|
{
|
||
|
if (guiWrapper.eventType != EventType.Repaint)
|
||
|
return;
|
||
|
|
||
|
DrawingUtility.EndLines();
|
||
|
Handles.color = m_TempColor;
|
||
|
}
|
||
|
|
||
|
public void DrawEdge(Vector2 startPosition, Vector2 endPosition)
|
||
|
{
|
||
|
DrawEdge(startPosition, endPosition, kEdgeColor);
|
||
|
}
|
||
|
|
||
|
public void DrawEdgeHovered(Vector2 startPosition, Vector2 endPosition)
|
||
|
{
|
||
|
DrawEdge(startPosition, endPosition, kEdgeHoveredColor);
|
||
|
}
|
||
|
|
||
|
public void DrawEdgeSelected(Vector2 startPosition, Vector2 endPosition)
|
||
|
{
|
||
|
DrawEdge(startPosition, endPosition, kEdgeSelectedColor);
|
||
|
}
|
||
|
|
||
|
public bool IsActionActive(MeshEditorAction action)
|
||
|
{
|
||
|
if (guiWrapper.isAltDown || !guiWrapper.IsControlHot(0))
|
||
|
return false;
|
||
|
|
||
|
var canCreateEdge = CanCreateEdge();
|
||
|
var canSplitEdge = CanSplitEdge();
|
||
|
|
||
|
if (action == MeshEditorAction.None)
|
||
|
return guiWrapper.IsControlNearest(defaultControlID);
|
||
|
|
||
|
if (action == MeshEditorAction.CreateVertex)
|
||
|
{
|
||
|
if(!frame.Contains(mouseWorldPosition))
|
||
|
return false;
|
||
|
|
||
|
if (mode == SpriteMeshViewMode.EditGeometry)
|
||
|
return guiWrapper.IsControlNearest(defaultControlID);
|
||
|
|
||
|
if (mode == SpriteMeshViewMode.CreateVertex)
|
||
|
return hoveredVertex == -1;
|
||
|
}
|
||
|
|
||
|
if (action == MeshEditorAction.MoveVertex)
|
||
|
return guiWrapper.IsControlNearest(m_HoveredVertexControlID);
|
||
|
|
||
|
if (action == MeshEditorAction.CreateEdge)
|
||
|
return canCreateEdge;
|
||
|
|
||
|
if (action == MeshEditorAction.SplitEdge)
|
||
|
return canSplitEdge;
|
||
|
|
||
|
if (action == MeshEditorAction.MoveEdge)
|
||
|
return guiWrapper.IsControlNearest(m_HoveredEdgeControlID);
|
||
|
|
||
|
if (action == MeshEditorAction.SelectVertex)
|
||
|
return guiWrapper.IsControlNearest(m_HoveredVertexControlID);
|
||
|
|
||
|
if (action == MeshEditorAction.SelectEdge)
|
||
|
return mode == SpriteMeshViewMode.EditGeometry &&
|
||
|
guiWrapper.IsControlNearest(m_HoveredEdgeControlID) &&
|
||
|
!canCreateEdge && !canSplitEdge;
|
||
|
|
||
|
if (action == MeshEditorAction.Remove)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool IsActionHot(MeshEditorAction action)
|
||
|
{
|
||
|
if (action == MeshEditorAction.None)
|
||
|
return guiWrapper.IsControlHot(0);
|
||
|
|
||
|
if (action == MeshEditorAction.MoveVertex)
|
||
|
return guiWrapper.IsControlHot(m_HoveredVertexControlID);
|
||
|
|
||
|
if (action == MeshEditorAction.MoveEdge)
|
||
|
return guiWrapper.IsControlHot(m_HoveredEdgeControlID);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool IsActionTriggered(MeshEditorAction action)
|
||
|
{
|
||
|
if (!IsActionActive(action))
|
||
|
return false;
|
||
|
|
||
|
if (action == MeshEditorAction.CreateVertex)
|
||
|
{
|
||
|
if (mode == SpriteMeshViewMode.EditGeometry)
|
||
|
return guiWrapper.IsMouseDown(0) && guiWrapper.clickCount == 2;
|
||
|
}
|
||
|
|
||
|
if (action == MeshEditorAction.Remove)
|
||
|
{
|
||
|
if ((guiWrapper.eventType == EventType.ValidateCommand || guiWrapper.eventType == EventType.ExecuteCommand)
|
||
|
&& (guiWrapper.commandName == kSoftDeleteCommandName || guiWrapper.commandName == kDeleteCommandName))
|
||
|
{
|
||
|
if (guiWrapper.eventType == EventType.ExecuteCommand)
|
||
|
return true;
|
||
|
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if(action != MeshEditorAction.None)
|
||
|
return guiWrapper.IsMouseDown(0);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public Vector2 WorldToScreen(Vector2 position)
|
||
|
{
|
||
|
return HandleUtility.WorldToGUIPoint(position);
|
||
|
}
|
||
|
|
||
|
private void ConsumeMouseMoveEvents()
|
||
|
{
|
||
|
if (guiWrapper.eventType == EventType.MouseMove || (guiWrapper.eventType == EventType.MouseDrag && guiWrapper.mouseButton == 0))
|
||
|
guiWrapper.UseCurrentEvent();
|
||
|
}
|
||
|
|
||
|
private bool CanCreateEdge()
|
||
|
{
|
||
|
if(!frame.Contains(mouseWorldPosition) || !(guiWrapper.IsControlNearest(defaultControlID) || guiWrapper.IsControlNearest(m_HoveredVertexControlID) || guiWrapper.IsControlNearest(m_HoveredEdgeControlID)))
|
||
|
return false;
|
||
|
|
||
|
if (mode == SpriteMeshViewMode.EditGeometry)
|
||
|
return guiWrapper.isShiftDown && selection.Count == 1 && !selection.Contains(hoveredVertex);
|
||
|
|
||
|
if (mode == SpriteMeshViewMode.CreateEdge)
|
||
|
return selection.Count == 1 && !selection.Contains(hoveredVertex);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private bool CanSplitEdge()
|
||
|
{
|
||
|
if(!frame.Contains(mouseWorldPosition) || !(guiWrapper.IsControlNearest(defaultControlID) || guiWrapper.IsControlNearest(m_HoveredEdgeControlID)))
|
||
|
return false;
|
||
|
|
||
|
if (mode == SpriteMeshViewMode.EditGeometry)
|
||
|
return guiWrapper.isShiftDown && m_NearestEdge != -1 && hoveredVertex == -1 && selection.Count == 0;
|
||
|
|
||
|
if (mode == SpriteMeshViewMode.SplitEdge)
|
||
|
return m_NearestEdge != -1 && hoveredVertex == -1;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private void DrawEdge(Vector2 startPosition, Vector2 endPosition, Color color)
|
||
|
{
|
||
|
if (guiWrapper.eventType != EventType.Repaint)
|
||
|
return;
|
||
|
|
||
|
Handles.color = color;
|
||
|
float width = kEdgeWidth / Handles.matrix.m00;
|
||
|
|
||
|
DrawingUtility.DrawSolidLine(width, startPosition, endPosition);
|
||
|
}
|
||
|
|
||
|
public void DoRepaint()
|
||
|
{
|
||
|
if(guiWrapper.eventType != EventType.Layout)
|
||
|
return;
|
||
|
|
||
|
var action = MeshEditorAction.None;
|
||
|
|
||
|
if(IsActionActive(MeshEditorAction.CreateVertex))
|
||
|
action = MeshEditorAction.CreateVertex;
|
||
|
else if(IsActionActive(MeshEditorAction.CreateEdge))
|
||
|
action = MeshEditorAction.CreateEdge;
|
||
|
else if(IsActionActive(MeshEditorAction.SplitEdge))
|
||
|
action = MeshEditorAction.SplitEdge;
|
||
|
|
||
|
if(m_PreviousActiveAction != action)
|
||
|
{
|
||
|
m_PreviousActiveAction = action;
|
||
|
guiWrapper.Repaint();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool CanRepaint()
|
||
|
{
|
||
|
return guiWrapper.eventType == EventType.Repaint;
|
||
|
}
|
||
|
|
||
|
public bool CanLayout()
|
||
|
{
|
||
|
return guiWrapper.eventType == EventType.Layout;
|
||
|
}
|
||
|
}
|
||
|
}
|