776 lines
28 KiB
C#
776 lines
28 KiB
C#
|
using System;
|
||
|
using Unity.Mathematics;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace UnityEditor.U2D.Animation
|
||
|
{
|
||
|
internal class SpriteMeshController
|
||
|
{
|
||
|
const float k_SnapDistance = 10f;
|
||
|
|
||
|
struct EdgeIntersectionResult
|
||
|
{
|
||
|
public int startVertexIndex;
|
||
|
public int endVertexIndex;
|
||
|
public int intersectEdgeIndex;
|
||
|
public Vector2 endPosition;
|
||
|
}
|
||
|
|
||
|
SpriteMeshDataController m_SpriteMeshDataController = new();
|
||
|
EdgeIntersectionResult m_EdgeIntersectionResult;
|
||
|
|
||
|
public ISpriteMeshView spriteMeshView { get; set; }
|
||
|
public BaseSpriteMeshData spriteMeshData { get; set; }
|
||
|
public ISelection<int> selection { get; set; }
|
||
|
public ICacheUndo cacheUndo { get; set; }
|
||
|
public ITriangulator triangulator { get; set; }
|
||
|
|
||
|
public bool disable { get; set; }
|
||
|
public Rect frame { get; set; }
|
||
|
|
||
|
public void OnGUI()
|
||
|
{
|
||
|
m_SpriteMeshDataController.spriteMeshData = spriteMeshData;
|
||
|
|
||
|
Debug.Assert(spriteMeshView != null);
|
||
|
Debug.Assert(spriteMeshData != null);
|
||
|
Debug.Assert(selection != null);
|
||
|
Debug.Assert(cacheUndo != null);
|
||
|
|
||
|
ValidateSelectionValues();
|
||
|
|
||
|
spriteMeshView.selection = selection;
|
||
|
spriteMeshView.frame = frame;
|
||
|
|
||
|
EditorGUI.BeginDisabledGroup(disable);
|
||
|
|
||
|
spriteMeshView.BeginLayout();
|
||
|
|
||
|
if (spriteMeshView.CanLayout())
|
||
|
{
|
||
|
LayoutVertices();
|
||
|
LayoutEdges();
|
||
|
}
|
||
|
|
||
|
spriteMeshView.EndLayout();
|
||
|
|
||
|
if (spriteMeshView.CanRepaint())
|
||
|
{
|
||
|
DrawEdges();
|
||
|
|
||
|
if (GUI.enabled)
|
||
|
{
|
||
|
PreviewCreateVertex();
|
||
|
PreviewCreateEdge();
|
||
|
PreviewSplitEdge();
|
||
|
}
|
||
|
|
||
|
DrawVertices();
|
||
|
}
|
||
|
|
||
|
|
||
|
HandleSplitEdge();
|
||
|
HandleCreateEdge();
|
||
|
HandleCreateVertex();
|
||
|
|
||
|
EditorGUI.EndDisabledGroup();
|
||
|
|
||
|
HandleSelectVertex();
|
||
|
HandleSelectEdge();
|
||
|
|
||
|
EditorGUI.BeginDisabledGroup(disable);
|
||
|
|
||
|
HandleMoveVertexAndEdge();
|
||
|
|
||
|
EditorGUI.EndDisabledGroup();
|
||
|
|
||
|
EditorGUI.BeginDisabledGroup(disable);
|
||
|
|
||
|
HandleRemoveVertices();
|
||
|
|
||
|
spriteMeshView.DoRepaint();
|
||
|
|
||
|
EditorGUI.EndDisabledGroup();
|
||
|
}
|
||
|
|
||
|
void ValidateSelectionValues()
|
||
|
{
|
||
|
foreach (var index in selection.elements)
|
||
|
{
|
||
|
if (index >= spriteMeshData.vertexCount)
|
||
|
{
|
||
|
selection.Clear();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LayoutVertices()
|
||
|
{
|
||
|
for (var i = 0; i < spriteMeshData.vertexCount; i++)
|
||
|
{
|
||
|
spriteMeshView.LayoutVertex(spriteMeshData.vertices[i], i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LayoutEdges()
|
||
|
{
|
||
|
for (var i = 0; i < spriteMeshData.edges.Length; i++)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[i];
|
||
|
var startPosition = spriteMeshData.vertices[edge.x];
|
||
|
var endPosition = spriteMeshData.vertices[edge.y];
|
||
|
|
||
|
spriteMeshView.LayoutEdge(startPosition, endPosition, i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DrawEdges()
|
||
|
{
|
||
|
UpdateEdgeIntersection();
|
||
|
|
||
|
spriteMeshView.BeginDrawEdges();
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.edges.Length; ++i)
|
||
|
{
|
||
|
if (SkipDrawEdge(i))
|
||
|
continue;
|
||
|
|
||
|
var edge = spriteMeshData.edges[i];
|
||
|
var startPosition = spriteMeshData.vertices[edge.x];
|
||
|
var endPosition = spriteMeshData.vertices[edge.y];
|
||
|
|
||
|
if (selection.Contains(edge.x) && selection.Contains(edge.y))
|
||
|
spriteMeshView.DrawEdgeSelected(startPosition, endPosition);
|
||
|
else
|
||
|
spriteMeshView.DrawEdge(startPosition, endPosition);
|
||
|
}
|
||
|
|
||
|
if (spriteMeshView.IsActionActive(MeshEditorAction.SelectEdge))
|
||
|
{
|
||
|
var hoveredEdge = spriteMeshData.edges[spriteMeshView.hoveredEdge];
|
||
|
var startPosition = spriteMeshData.vertices[hoveredEdge.x];
|
||
|
var endPosition = spriteMeshData.vertices[hoveredEdge.y];
|
||
|
|
||
|
spriteMeshView.DrawEdgeHovered(startPosition, endPosition);
|
||
|
}
|
||
|
|
||
|
spriteMeshView.EndDrawEdges();
|
||
|
}
|
||
|
|
||
|
bool SkipDrawEdge(int edgeIndex)
|
||
|
{
|
||
|
if (GUI.enabled == false)
|
||
|
return false;
|
||
|
|
||
|
return edgeIndex == -1 ||
|
||
|
spriteMeshView.hoveredEdge == edgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.SelectEdge) ||
|
||
|
spriteMeshView.hoveredEdge == edgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.CreateVertex) ||
|
||
|
spriteMeshView.closestEdge == edgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.SplitEdge) ||
|
||
|
edgeIndex == m_EdgeIntersectionResult.intersectEdgeIndex && spriteMeshView.IsActionActive(MeshEditorAction.CreateEdge);
|
||
|
}
|
||
|
|
||
|
void PreviewCreateVertex()
|
||
|
{
|
||
|
if (spriteMeshView.mode == SpriteMeshViewMode.CreateVertex &&
|
||
|
spriteMeshView.IsActionActive(MeshEditorAction.CreateVertex))
|
||
|
{
|
||
|
var clampedMousePos = ClampToFrame(spriteMeshView.mouseWorldPosition);
|
||
|
|
||
|
if (spriteMeshView.hoveredEdge != -1)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[spriteMeshView.hoveredEdge];
|
||
|
|
||
|
spriteMeshView.BeginDrawEdges();
|
||
|
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[edge.x], clampedMousePos);
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[edge.y], clampedMousePos);
|
||
|
|
||
|
spriteMeshView.EndDrawEdges();
|
||
|
}
|
||
|
|
||
|
spriteMeshView.DrawVertex(clampedMousePos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PreviewCreateEdge()
|
||
|
{
|
||
|
if (!spriteMeshView.IsActionActive(MeshEditorAction.CreateEdge))
|
||
|
return;
|
||
|
|
||
|
spriteMeshView.BeginDrawEdges();
|
||
|
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[m_EdgeIntersectionResult.startVertexIndex], m_EdgeIntersectionResult.endPosition);
|
||
|
|
||
|
if (m_EdgeIntersectionResult.intersectEdgeIndex != -1)
|
||
|
{
|
||
|
var intersectingEdge = spriteMeshData.edges[m_EdgeIntersectionResult.intersectEdgeIndex];
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[intersectingEdge.x], m_EdgeIntersectionResult.endPosition);
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[intersectingEdge.y], m_EdgeIntersectionResult.endPosition);
|
||
|
}
|
||
|
|
||
|
spriteMeshView.EndDrawEdges();
|
||
|
|
||
|
if (m_EdgeIntersectionResult.endVertexIndex == -1)
|
||
|
spriteMeshView.DrawVertex(m_EdgeIntersectionResult.endPosition);
|
||
|
}
|
||
|
|
||
|
void PreviewSplitEdge()
|
||
|
{
|
||
|
if (!spriteMeshView.IsActionActive(MeshEditorAction.SplitEdge))
|
||
|
return;
|
||
|
|
||
|
var clampedMousePos = ClampToFrame(spriteMeshView.mouseWorldPosition);
|
||
|
|
||
|
var closestEdge = spriteMeshData.edges[spriteMeshView.closestEdge];
|
||
|
|
||
|
spriteMeshView.BeginDrawEdges();
|
||
|
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[closestEdge.x], clampedMousePos);
|
||
|
spriteMeshView.DrawEdge(spriteMeshData.vertices[closestEdge.y], clampedMousePos);
|
||
|
|
||
|
spriteMeshView.EndDrawEdges();
|
||
|
|
||
|
spriteMeshView.DrawVertex(clampedMousePos);
|
||
|
}
|
||
|
|
||
|
void DrawVertices()
|
||
|
{
|
||
|
for (var i = 0; i < spriteMeshData.vertexCount; i++)
|
||
|
{
|
||
|
var position = spriteMeshData.vertices[i];
|
||
|
|
||
|
if (selection.Contains(i))
|
||
|
spriteMeshView.DrawVertexSelected(position);
|
||
|
else if (i == spriteMeshView.hoveredVertex && spriteMeshView.IsActionHot(MeshEditorAction.None))
|
||
|
spriteMeshView.DrawVertexHovered(position);
|
||
|
else
|
||
|
spriteMeshView.DrawVertex(position);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HandleSelectVertex()
|
||
|
{
|
||
|
if (spriteMeshView.DoSelectVertex(out var additive))
|
||
|
SelectVertex(spriteMeshView.hoveredVertex, additive);
|
||
|
}
|
||
|
|
||
|
void HandleSelectEdge()
|
||
|
{
|
||
|
if (spriteMeshView.DoSelectEdge(out var additive))
|
||
|
SelectEdge(spriteMeshView.hoveredEdge, additive);
|
||
|
}
|
||
|
|
||
|
void HandleMoveVertexAndEdge()
|
||
|
{
|
||
|
if (selection.Count == 0)
|
||
|
return;
|
||
|
|
||
|
if (spriteMeshView.DoMoveVertex(out var finalDeltaPos) || spriteMeshView.DoMoveEdge(out finalDeltaPos))
|
||
|
{
|
||
|
var selectionArray = selection.elements;
|
||
|
|
||
|
finalDeltaPos = MathUtility.MoveRectInsideFrame(CalculateRectFromSelection(), frame, finalDeltaPos);
|
||
|
var movedVertexSelection = GetMovedVertexSelection(in selectionArray, spriteMeshData.vertices, finalDeltaPos);
|
||
|
|
||
|
if (IsMovedEdgeIntersectingWithOtherEdge(in selectionArray, in movedVertexSelection, spriteMeshData.edges, spriteMeshData.vertices))
|
||
|
return;
|
||
|
if (IsMovedVertexIntersectingWithOutline(in selectionArray, in movedVertexSelection, spriteMeshData.outlineEdges, spriteMeshData.vertices))
|
||
|
return;
|
||
|
|
||
|
cacheUndo.BeginUndoOperation(TextContent.moveVertices);
|
||
|
MoveSelectedVertices(in movedVertexSelection);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HandleCreateVertex()
|
||
|
{
|
||
|
if (spriteMeshView.DoCreateVertex())
|
||
|
{
|
||
|
var position = ClampToFrame(spriteMeshView.mouseWorldPosition);
|
||
|
var edgeIndex = spriteMeshView.hoveredEdge;
|
||
|
if (spriteMeshView.hoveredEdge != -1)
|
||
|
CreateVertex(position, edgeIndex);
|
||
|
else if (m_SpriteMeshDataController.FindTriangle(position, out var indices, out var barycentricCoords))
|
||
|
CreateVertex(position, indices, barycentricCoords);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HandleSplitEdge()
|
||
|
{
|
||
|
if (spriteMeshView.DoSplitEdge())
|
||
|
SplitEdge(ClampToFrame(spriteMeshView.mouseWorldPosition), spriteMeshView.closestEdge);
|
||
|
}
|
||
|
|
||
|
void HandleCreateEdge()
|
||
|
{
|
||
|
if (spriteMeshView.DoCreateEdge())
|
||
|
{
|
||
|
var clampedMousePosition = ClampToFrame(spriteMeshView.mouseWorldPosition);
|
||
|
var edgeIntersectionResult = CalculateEdgeIntersection(selection.activeElement, spriteMeshView.hoveredVertex, spriteMeshView.hoveredEdge, clampedMousePosition);
|
||
|
|
||
|
if (edgeIntersectionResult.endVertexIndex != -1)
|
||
|
{
|
||
|
CreateEdge(selection.activeElement, edgeIntersectionResult.endVertexIndex);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (edgeIntersectionResult.intersectEdgeIndex != -1)
|
||
|
{
|
||
|
CreateVertex(edgeIntersectionResult.endPosition, edgeIntersectionResult.intersectEdgeIndex);
|
||
|
CreateEdge(selection.activeElement, spriteMeshData.vertexCount - 1);
|
||
|
}
|
||
|
else if (m_SpriteMeshDataController.FindTriangle(edgeIntersectionResult.endPosition, out var indices, out var barycentricCoords))
|
||
|
{
|
||
|
CreateVertex(edgeIntersectionResult.endPosition, indices, barycentricCoords);
|
||
|
CreateEdge(selection.activeElement, spriteMeshData.vertexCount - 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HandleRemoveVertices()
|
||
|
{
|
||
|
if (spriteMeshView.DoRemove())
|
||
|
RemoveSelectedVertices();
|
||
|
}
|
||
|
|
||
|
void CreateVertex(Vector2 position, Vector3Int indices, Vector3 barycentricCoords)
|
||
|
{
|
||
|
var bw1 = spriteMeshData.vertexWeights[indices.x];
|
||
|
var bw2 = spriteMeshData.vertexWeights[indices.y];
|
||
|
var bw3 = spriteMeshData.vertexWeights[indices.z];
|
||
|
|
||
|
var result = new EditableBoneWeight();
|
||
|
|
||
|
foreach (var channel in bw1)
|
||
|
{
|
||
|
if (!channel.enabled)
|
||
|
continue;
|
||
|
|
||
|
var weight = channel.weight * barycentricCoords.x;
|
||
|
if (weight > 0f)
|
||
|
result.AddChannel(channel.boneIndex, weight, true);
|
||
|
}
|
||
|
|
||
|
foreach (var channel in bw2)
|
||
|
{
|
||
|
if (!channel.enabled)
|
||
|
continue;
|
||
|
|
||
|
var weight = channel.weight * barycentricCoords.y;
|
||
|
if (weight > 0f)
|
||
|
result.AddChannel(channel.boneIndex, weight, true);
|
||
|
}
|
||
|
|
||
|
foreach (var channel in bw3)
|
||
|
{
|
||
|
if (!channel.enabled)
|
||
|
continue;
|
||
|
|
||
|
var weight = channel.weight * barycentricCoords.z;
|
||
|
if (weight > 0f)
|
||
|
result.AddChannel(channel.boneIndex, weight, true);
|
||
|
}
|
||
|
|
||
|
result.UnifyChannelsWithSameBoneIndex();
|
||
|
result.FilterChannels(0f);
|
||
|
result.Clamp(4, true);
|
||
|
|
||
|
var boneWeight = result.ToBoneWeight(true);
|
||
|
|
||
|
cacheUndo.BeginUndoOperation(TextContent.createVertex);
|
||
|
|
||
|
m_SpriteMeshDataController.CreateVertex(position, -1);
|
||
|
spriteMeshData.vertexWeights[spriteMeshData.vertexCount - 1].SetFromBoneWeight(boneWeight);
|
||
|
Triangulate();
|
||
|
}
|
||
|
|
||
|
void CreateVertex(Vector2 position, int edgeIndex)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[edgeIndex];
|
||
|
var pos1 = spriteMeshData.vertices[edge.x];
|
||
|
var pos2 = spriteMeshData.vertices[edge.y];
|
||
|
var dir1 = (position - pos1);
|
||
|
var dir2 = (pos2 - pos1);
|
||
|
var t = Vector2.Dot(dir1, dir2.normalized) / dir2.magnitude;
|
||
|
t = Mathf.Clamp01(t);
|
||
|
var bw1 = spriteMeshData.vertexWeights[edge.x].ToBoneWeight(true);
|
||
|
var bw2 = spriteMeshData.vertexWeights[edge.y].ToBoneWeight(true);
|
||
|
|
||
|
var boneWeight = EditableBoneWeightUtility.Lerp(bw1, bw2, t);
|
||
|
|
||
|
cacheUndo.BeginUndoOperation(TextContent.createVertex);
|
||
|
|
||
|
m_SpriteMeshDataController.CreateVertex(position, edgeIndex);
|
||
|
spriteMeshData.vertexWeights[spriteMeshData.vertexCount - 1].SetFromBoneWeight(boneWeight);
|
||
|
Triangulate();
|
||
|
}
|
||
|
|
||
|
void SelectVertex(int index, bool additiveToggle)
|
||
|
{
|
||
|
if (index < 0)
|
||
|
throw new ArgumentException("Index out of range");
|
||
|
|
||
|
var selected = selection.Contains(index);
|
||
|
if (selected)
|
||
|
{
|
||
|
if (additiveToggle)
|
||
|
{
|
||
|
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||
|
selection.Select(index, false);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||
|
|
||
|
if (!additiveToggle)
|
||
|
ClearSelection();
|
||
|
|
||
|
selection.Select(index, true);
|
||
|
}
|
||
|
|
||
|
cacheUndo.IncrementCurrentGroup();
|
||
|
}
|
||
|
|
||
|
void SelectEdge(int index, bool additiveToggle)
|
||
|
{
|
||
|
Debug.Assert(index >= 0);
|
||
|
|
||
|
var edge = spriteMeshData.edges[index];
|
||
|
|
||
|
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||
|
|
||
|
var selected = selection.Contains(edge.x) && selection.Contains(edge.y);
|
||
|
if (selected)
|
||
|
{
|
||
|
if (additiveToggle)
|
||
|
{
|
||
|
selection.Select(edge.x, false);
|
||
|
selection.Select(edge.y, false);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!additiveToggle)
|
||
|
ClearSelection();
|
||
|
|
||
|
selection.Select(edge.x, true);
|
||
|
selection.Select(edge.y, true);
|
||
|
}
|
||
|
|
||
|
cacheUndo.IncrementCurrentGroup();
|
||
|
}
|
||
|
|
||
|
void ClearSelection()
|
||
|
{
|
||
|
cacheUndo.BeginUndoOperation(TextContent.selection);
|
||
|
selection.Clear();
|
||
|
}
|
||
|
|
||
|
void MoveSelectedVertices(in Vector2[] movedVertices)
|
||
|
{
|
||
|
for (var i = 0; i < selection.Count; ++i)
|
||
|
{
|
||
|
var index = selection.elements[i];
|
||
|
spriteMeshData.vertices[index] = movedVertices[i];
|
||
|
}
|
||
|
|
||
|
Triangulate();
|
||
|
}
|
||
|
|
||
|
void CreateEdge(int fromVertexIndex, int toVertexIndex)
|
||
|
{
|
||
|
cacheUndo.BeginUndoOperation(TextContent.createEdge);
|
||
|
|
||
|
m_SpriteMeshDataController.CreateEdge(fromVertexIndex, toVertexIndex);
|
||
|
Triangulate();
|
||
|
ClearSelection();
|
||
|
selection.Select(toVertexIndex, true);
|
||
|
|
||
|
cacheUndo.IncrementCurrentGroup();
|
||
|
}
|
||
|
|
||
|
void SplitEdge(Vector2 position, int edgeIndex)
|
||
|
{
|
||
|
cacheUndo.BeginUndoOperation(TextContent.splitEdge);
|
||
|
|
||
|
CreateVertex(position, edgeIndex);
|
||
|
|
||
|
cacheUndo.IncrementCurrentGroup();
|
||
|
}
|
||
|
|
||
|
bool IsEdgeSelected()
|
||
|
{
|
||
|
if (selection.Count != 2)
|
||
|
return false;
|
||
|
|
||
|
var indices = selection.elements;
|
||
|
|
||
|
var index1 = indices[0];
|
||
|
var index2 = indices[1];
|
||
|
|
||
|
var edge = new int2(index1, index2);
|
||
|
return spriteMeshData.edges.ContainsAny(edge);
|
||
|
}
|
||
|
|
||
|
void RemoveSelectedVertices()
|
||
|
{
|
||
|
cacheUndo.BeginUndoOperation(IsEdgeSelected() ? TextContent.removeEdge : TextContent.removeVertices);
|
||
|
|
||
|
var verticesToRemove = selection.elements;
|
||
|
|
||
|
var noOfVertsToDelete = verticesToRemove.Length;
|
||
|
var noOfVertsInMesh = m_SpriteMeshDataController.spriteMeshData.vertexCount;
|
||
|
var shouldClearMesh = (noOfVertsInMesh - noOfVertsToDelete) < 3;
|
||
|
|
||
|
if (shouldClearMesh)
|
||
|
{
|
||
|
m_SpriteMeshDataController.spriteMeshData.Clear();
|
||
|
m_SpriteMeshDataController.CreateQuad();
|
||
|
}
|
||
|
else
|
||
|
m_SpriteMeshDataController.RemoveVertex(verticesToRemove);
|
||
|
|
||
|
Triangulate();
|
||
|
|
||
|
selection.Clear();
|
||
|
}
|
||
|
|
||
|
void Triangulate()
|
||
|
{
|
||
|
m_SpriteMeshDataController.Triangulate(triangulator);
|
||
|
m_SpriteMeshDataController.SortTrianglesByDepth();
|
||
|
}
|
||
|
|
||
|
Vector2 ClampToFrame(Vector2 position)
|
||
|
{
|
||
|
return MathUtility.ClampPositionToRect(position, frame);
|
||
|
}
|
||
|
|
||
|
Rect CalculateRectFromSelection()
|
||
|
{
|
||
|
var rect = new Rect();
|
||
|
|
||
|
var min = new Vector2(float.MaxValue, float.MaxValue);
|
||
|
var max = new Vector2(float.MinValue, float.MinValue);
|
||
|
|
||
|
var indices = selection.elements;
|
||
|
|
||
|
foreach (var index in indices)
|
||
|
{
|
||
|
var v = spriteMeshData.vertices[index];
|
||
|
|
||
|
min.x = Mathf.Min(min.x, v.x);
|
||
|
min.y = Mathf.Min(min.y, v.y);
|
||
|
|
||
|
max.x = Mathf.Max(max.x, v.x);
|
||
|
max.y = Mathf.Max(max.y, v.y);
|
||
|
}
|
||
|
|
||
|
rect.min = min;
|
||
|
rect.max = max;
|
||
|
|
||
|
return rect;
|
||
|
}
|
||
|
|
||
|
void UpdateEdgeIntersection()
|
||
|
{
|
||
|
if (selection.Count == 1)
|
||
|
m_EdgeIntersectionResult = CalculateEdgeIntersection(selection.activeElement, spriteMeshView.hoveredVertex, spriteMeshView.hoveredEdge, ClampToFrame(spriteMeshView.mouseWorldPosition));
|
||
|
}
|
||
|
|
||
|
EdgeIntersectionResult CalculateEdgeIntersection(int vertexIndex, int hoveredVertexIndex, int hoveredEdgeIndex, Vector2 targetPosition)
|
||
|
{
|
||
|
Debug.Assert(vertexIndex >= 0);
|
||
|
|
||
|
var edgeIntersection = new EdgeIntersectionResult
|
||
|
{
|
||
|
startVertexIndex = vertexIndex,
|
||
|
endVertexIndex = hoveredVertexIndex,
|
||
|
endPosition = targetPosition,
|
||
|
intersectEdgeIndex = -1
|
||
|
};
|
||
|
|
||
|
var startPoint = spriteMeshData.vertices[edgeIntersection.startVertexIndex];
|
||
|
|
||
|
var intersectsEdge = false;
|
||
|
var lastIntersectingEdgeIndex = -1;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
lastIntersectingEdgeIndex = edgeIntersection.intersectEdgeIndex;
|
||
|
|
||
|
if (intersectsEdge)
|
||
|
{
|
||
|
var dir = edgeIntersection.endPosition - startPoint;
|
||
|
edgeIntersection.endPosition += dir.normalized * 10f;
|
||
|
}
|
||
|
|
||
|
intersectsEdge = SegmentIntersectsEdge(startPoint, edgeIntersection.endPosition, vertexIndex, ref edgeIntersection.endPosition, out edgeIntersection.intersectEdgeIndex);
|
||
|
|
||
|
//if we are hovering a vertex and intersect an edge indexing it we forget about the intersection
|
||
|
var edges = spriteMeshData.edges;
|
||
|
var edge = intersectsEdge ? edges[edgeIntersection.intersectEdgeIndex] : default;
|
||
|
if (intersectsEdge && (edge.x == edgeIntersection.endVertexIndex || edge.y == edgeIntersection.endVertexIndex))
|
||
|
{
|
||
|
edgeIntersection.intersectEdgeIndex = -1;
|
||
|
intersectsEdge = false;
|
||
|
edgeIntersection.endPosition = spriteMeshData.vertices[edgeIntersection.endVertexIndex];
|
||
|
}
|
||
|
|
||
|
if (intersectsEdge)
|
||
|
{
|
||
|
edgeIntersection.endVertexIndex = -1;
|
||
|
|
||
|
var intersectingEdge = spriteMeshData.edges[edgeIntersection.intersectEdgeIndex];
|
||
|
var newPointScreen = spriteMeshView.WorldToScreen(edgeIntersection.endPosition);
|
||
|
var edgeV1 = spriteMeshView.WorldToScreen(spriteMeshData.vertices[intersectingEdge.x]);
|
||
|
var edgeV2 = spriteMeshView.WorldToScreen(spriteMeshData.vertices[intersectingEdge.y]);
|
||
|
|
||
|
if ((newPointScreen - edgeV1).magnitude <= k_SnapDistance)
|
||
|
edgeIntersection.endVertexIndex = intersectingEdge.x;
|
||
|
else if ((newPointScreen - edgeV2).magnitude <= k_SnapDistance)
|
||
|
edgeIntersection.endVertexIndex = intersectingEdge.y;
|
||
|
|
||
|
if (edgeIntersection.endVertexIndex != -1)
|
||
|
{
|
||
|
edgeIntersection.intersectEdgeIndex = -1;
|
||
|
intersectsEdge = false;
|
||
|
edgeIntersection.endPosition = spriteMeshData.vertices[edgeIntersection.endVertexIndex];
|
||
|
}
|
||
|
}
|
||
|
} while (intersectsEdge && lastIntersectingEdgeIndex != edgeIntersection.intersectEdgeIndex);
|
||
|
|
||
|
edgeIntersection.intersectEdgeIndex = intersectsEdge ? edgeIntersection.intersectEdgeIndex : hoveredEdgeIndex;
|
||
|
|
||
|
if (edgeIntersection.endVertexIndex != -1 && !intersectsEdge)
|
||
|
edgeIntersection.endPosition = spriteMeshData.vertices[edgeIntersection.endVertexIndex];
|
||
|
|
||
|
return edgeIntersection;
|
||
|
}
|
||
|
|
||
|
bool SegmentIntersectsEdge(Vector2 p1, Vector2 p2, int ignoreIndex, ref Vector2 point, out int intersectingEdgeIndex)
|
||
|
{
|
||
|
intersectingEdgeIndex = -1;
|
||
|
|
||
|
var sqrDistance = float.MaxValue;
|
||
|
|
||
|
for (var i = 0; i < spriteMeshData.edges.Length; i++)
|
||
|
{
|
||
|
var edge = spriteMeshData.edges[i];
|
||
|
var v1 = spriteMeshData.vertices[edge.x];
|
||
|
var v2 = spriteMeshData.vertices[edge.y];
|
||
|
var pointTmp = Vector2.zero;
|
||
|
|
||
|
if (edge.x != ignoreIndex && edge.y != ignoreIndex &&
|
||
|
MathUtility.SegmentIntersection(p1, p2, v1, v2, ref pointTmp))
|
||
|
{
|
||
|
var sqrMagnitude = (pointTmp - p1).sqrMagnitude;
|
||
|
if (sqrMagnitude < sqrDistance)
|
||
|
{
|
||
|
sqrDistance = sqrMagnitude;
|
||
|
intersectingEdgeIndex = i;
|
||
|
point = pointTmp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return intersectingEdgeIndex != -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static Vector2[] GetMovedVertexSelection(in int[] selection, in Vector2[] vertices, Vector2 deltaPosition)
|
||
|
{
|
||
|
var movedVertices = new Vector2[selection.Length];
|
||
|
for (var i = 0; i < selection.Length; i++)
|
||
|
{
|
||
|
var index = selection[i];
|
||
|
movedVertices[i] = vertices[index] + deltaPosition;
|
||
|
}
|
||
|
return movedVertices;
|
||
|
}
|
||
|
|
||
|
static bool IsMovedEdgeIntersectingWithOtherEdge(in int[] selection, in Vector2[] movedVertices, in int2[] meshEdges, in Vector2[] meshVertices)
|
||
|
{
|
||
|
var edgeCount = meshEdges.Length;
|
||
|
var edgeIntersectionPoint = Vector2.zero;
|
||
|
|
||
|
for (var i = 0; i < edgeCount; i++)
|
||
|
{
|
||
|
var selectionIndex = FindSelectionIndexFromEdge(selection, meshEdges[i]);
|
||
|
if (selectionIndex.x == -1 && selectionIndex.y == -1)
|
||
|
continue;
|
||
|
|
||
|
var edgeStart = selectionIndex.x != -1 ? movedVertices[selectionIndex.x] : meshVertices[meshEdges[i].x];
|
||
|
var edgeEnd = selectionIndex.y != -1 ? movedVertices[selectionIndex.y] : meshVertices[meshEdges[i].y];
|
||
|
|
||
|
for (var o = 0; o < edgeCount; o++)
|
||
|
{
|
||
|
if (o == i)
|
||
|
continue;
|
||
|
|
||
|
if (meshEdges[i].x == meshEdges[o].x || meshEdges[i].y == meshEdges[o].x ||
|
||
|
meshEdges[i].x == meshEdges[o].y || meshEdges[i].y == meshEdges[o].y)
|
||
|
continue;
|
||
|
|
||
|
var otherSelectionIndex = FindSelectionIndexFromEdge(in selection, meshEdges[o]);
|
||
|
var otherEdgeStart = otherSelectionIndex.x != -1 ? movedVertices[otherSelectionIndex.x] : meshVertices[meshEdges[o].x];
|
||
|
var otherEdgeEnd = otherSelectionIndex.y != -1 ? movedVertices[otherSelectionIndex.y] : meshVertices[meshEdges[o].y];
|
||
|
|
||
|
if (MathUtility.SegmentIntersection(edgeStart, edgeEnd, otherEdgeStart, otherEdgeEnd, ref edgeIntersectionPoint))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static int2 FindSelectionIndexFromEdge(in int[] selection, int2 edge)
|
||
|
{
|
||
|
var selectionIndex = new int2(-1, -1);
|
||
|
for (var m = 0; m < selection.Length; ++m)
|
||
|
{
|
||
|
if (selection[m] == edge.x)
|
||
|
{
|
||
|
selectionIndex.x = m;
|
||
|
break;
|
||
|
}
|
||
|
if (selection[m] == edge.y)
|
||
|
{
|
||
|
selectionIndex.y = m;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return selectionIndex;
|
||
|
}
|
||
|
|
||
|
static bool IsMovedVertexIntersectingWithOutline(in int[] selection, in Vector2[] movedVertices, in int2[] outlineEdges, in Vector2[] meshVertices)
|
||
|
{
|
||
|
var edgeIntersectionPoint = Vector2.zero;
|
||
|
|
||
|
for (var i = 0; i < selection.Length; ++i)
|
||
|
{
|
||
|
var edgeStart = meshVertices[selection[i]];
|
||
|
var edgeEnd = movedVertices[i];
|
||
|
|
||
|
for (var m = 0; m < outlineEdges.Length; ++m)
|
||
|
{
|
||
|
if (selection[i] == outlineEdges[m].x || selection[i] == outlineEdges[m].y)
|
||
|
continue;
|
||
|
|
||
|
var otherSelectionIndex = FindSelectionIndexFromEdge(in selection, outlineEdges[m]);
|
||
|
var otherEdgeStart = otherSelectionIndex.x != -1 ? movedVertices[otherSelectionIndex.x] : meshVertices[outlineEdges[m].x];
|
||
|
var otherEdgeEnd = otherSelectionIndex.y != -1 ? movedVertices[otherSelectionIndex.y] : meshVertices[outlineEdges[m].y];
|
||
|
|
||
|
if (MathUtility.SegmentIntersection(edgeStart, edgeEnd, otherEdgeStart, otherEdgeEnd, ref edgeIntersectionPoint))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|