本文已参与「新人创作礼」活动,一起开启掘金创作之路。
布料缝合--(一)选取Mesh缝合边界顶点 - 掘金 (juejin.cn)
布料缝合--(二)绘制缝线
布料缝合--(三)重置Mesh顶点、三角面 - 掘金 (juejin.cn)
缝线类:
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 显示缝线的类
///
/// 共5种类型的缝线: EdgeA、EdgeB为两个缝边上的, OuterOne、OuterOther为两端显示封边范围, Inner为内部缝线
/// </summary>
public class SewingLine : MonoBehaviour
{
public Color EdgeColor;
public Color BorderColor;
public Color InnerColor;
public float EdgeThickness;
public float BorderThickness;
public float InnerThickness;
private Transform m_edgeA;
private Transform m_edgeB;
private Vector3 lastEdgeAPos;
private Vector3 lastEdgeBPos;
private int lastSewCountA;
private int lastSewCountB;
private VrGizmos.DrawCommand m_cmdEdgeA;
private VrGizmos.DrawCommand m_cmdEdgeB;
private VrGizmos.DrawCommand m_cmdBorderA;
private VrGizmos.DrawCommand m_cmdBorderB;
private VrGizmos.DrawCommand m_cmdInner;
private Vector3[] m_vertsA;
private Vector3[] m_vertsB;
private List<int> m_sewingIndexA;
private List<int> m_sewingIndexB;
//初始化
public void Init()
{
lastSewCountA = 0;
lastSewCountB = 0;
m_cmdEdgeA = null;
m_cmdEdgeB = null;
m_cmdBorderA = null;
m_cmdBorderB = null;
m_cmdInner = null;
m_vertsA = null;
m_vertsB = null;
m_sewingIndexA = null;
m_sewingIndexB = null;
}
private void Update()
{
var needRefreshInnere = false;
if (m_edgeA != null && lastEdgeAPos != m_edgeA.position)
{
needRefreshInnere = true;
UpdateDrawCommand(m_edgeA, m_vertsA, m_sewingIndexA, ref m_cmdEdgeA, ref m_cmdBorderA);
lastEdgeAPos = m_edgeA.position;
}
if (m_edgeB != null && lastEdgeBPos != m_edgeB.position)
{
needRefreshInnere = true;
UpdateDrawCommand(m_edgeB, m_vertsB, m_sewingIndexB, ref m_cmdEdgeB, ref m_cmdBorderB);
lastEdgeBPos = m_edgeB.position;
}
if (needRefreshInnere)
{
UpdateSewingLines(m_edgeA, m_edgeB,
m_vertsA, m_vertsB,
m_sewingIndexA, m_sewingIndexB,
true
);
}
}
private bool edgeHasChanged = false;
public void UpdateSewingEdgeA(Transform _edgeA, Vector3[] verticesA, List<int> sewingIndexA)
{
if (m_edgeA == null)
{
m_edgeA = _edgeA;
lastEdgeAPos = m_edgeA.position;
}
var count = sewingIndexA.Count;
if (count != 0 && lastSewCountA != count)
{
m_vertsA = verticesA;
m_sewingIndexA = sewingIndexA;
lastSewCountA = count;
edgeHasChanged = true;
UpdateDrawCommand(_edgeA, verticesA, sewingIndexA, ref m_cmdEdgeA, ref m_cmdBorderA);
}
if (m_cmdEdgeA != null)
VrGizmos.AddDraw(m_cmdEdgeA);
if (m_cmdBorderA != null)
VrGizmos.AddDraw(m_cmdBorderA);
}
public void UpdateSewingEdgeB(Transform _edgeB, Vector3[] verticesB, List<int> sewingIndexB)
{
if (m_edgeB == null)
{
m_edgeB = _edgeB;
lastEdgeBPos = m_edgeB.position;
}
var count = sewingIndexB.Count;
if (count != 0 && lastSewCountB != count)
{
m_vertsB = verticesB;
m_sewingIndexB = sewingIndexB;
lastSewCountB = count;
edgeHasChanged = true;
UpdateDrawCommand(_edgeB, verticesB, sewingIndexB, ref m_cmdEdgeB, ref m_cmdBorderB);
}
if (m_cmdEdgeB != null)
VrGizmos.AddDraw(m_cmdEdgeB);
if (m_cmdBorderB != null)
VrGizmos.AddDraw(m_cmdBorderB);
}
private void UpdateDrawCommand(Transform edge, Vector3[] verts, List<int> sewingIndex,
ref VrGizmos.DrawCommand EdgeCmd, ref VrGizmos.DrawCommand BorderCmd)
{
if (EdgeCmd != null)
VrGizmos.RemoveDraw(EdgeCmd);
var count = sewingIndex.Count;
VrGizmos.Segment[] segments = new VrGizmos.Segment[count - 1];
for (int i = 0; i < count - 1; i++)
{
var start = edge.TransformPoint(verts[sewingIndex[i]]);
var end = edge.TransformPoint(verts[sewingIndex[i + 1]]);
segments[i] = new VrGizmos.Segment(start, end, EdgeColor);
}
EdgeCmd = VrGizmos.DrawSegments(segments, EdgeThickness);
VrGizmos.AddDraw(EdgeCmd);
//两端的端点
if (BorderCmd != null)
VrGizmos.RemoveDraw(BorderCmd);
Vector3[] borderPoints = new Vector3[2];
borderPoints[0] = edge.TransformPoint(verts[sewingIndex[0]]);
borderPoints[1] = edge.TransformPoint(verts[sewingIndex[sewingIndex.Count - 1]]);
Quaternion[] borderPointRotas = new Quaternion[2];
borderPointRotas[0] = Quaternion.FromToRotation(Vector3.up,
edge.TransformPoint(verts[sewingIndex[1]]) - borderPoints[0]);
;
borderPointRotas[1] = Quaternion.FromToRotation(Vector3.up,
borderPoints[1] - edge.TransformPoint(verts[sewingIndex[sewingIndex.Count - 2]]));
;
BorderCmd = VrGizmos.DrawBox(borderPoints, borderPointRotas, Vector3.one * BorderThickness, BorderColor);
}
public void UpdateSewingLines(
Transform _edgeA, Transform _edgeB,
Vector3[] verticesA, Vector3[] verticesB,
List<int> sewingIndexA, List<int> sewingIndexB,
bool refresh)
{
if (refresh || (edgeHasChanged && (sewingIndexA.Count >= 2 && sewingIndexB.Count >= 2)))
{
/*
//两端的缝线
if (_cmdBorder != null)
{
VrGizmos.RemoveDraw(_cmdBorder);
}
VrGizmos.Segment[] borderSegs = new VrGizmos.Segment[2];
var start0 = _edgeA.TransformPoint(verticesA[sewingIndexA[0]]);
var end0 = _edgeB.TransformPoint(verticesB[sewingIndexB[0]]);
var start1 = _edgeA.TransformPoint(verticesA[sewingIndexA[sewingIndexA.Count - 1]]);
var end1 = _edgeB.TransformPoint(verticesB[sewingIndexB[sewingIndexB.Count - 1]]);
borderSegs[0] = new VrGizmos.Segment(start0, end0, BorderColor);
borderSegs[1] = new VrGizmos.Segment(start1, end1, BorderColor);
_cmdBorder = VrGizmos.DrawSegments(borderSegs, BorderThickness);
*/
//如果A、B都只有两个点,则没有内部线
if (!(sewingIndexA.Count == 2 && sewingIndexB.Count == 2))
{
//内部的缝线
if (m_cmdInner != null)
VrGizmos.RemoveDraw(m_cmdInner);
List<int> _long, _short;
Transform _longTrans, _shortTrans;
Vector3[] _longVerts, _shortVerts;
if (sewingIndexA.Count >= sewingIndexB.Count)
{
_long = sewingIndexA;
_short = sewingIndexB;
_longTrans = _edgeA;
_shortTrans = _edgeB;
_longVerts = verticesA;
_shortVerts = verticesB;
}
else
{
_long = sewingIndexB;
_short = sewingIndexA;
_longTrans = _edgeB;
_shortTrans = _edgeA;
_longVerts = verticesB;
_shortVerts = verticesA;
}
float factor = (float) _long.Count / _short.Count;
VrGizmos.Segment[] innerSegs = new VrGizmos.Segment[_long.Count];
for (int i = 0; i < _long.Count; i++)
{
var start = _longTrans.TransformPoint(_longVerts[_long[i]]);
var end = _shortTrans.TransformPoint(_shortVerts[_short[(int) (i / factor)]]);
innerSegs[i] = new VrGizmos.Segment(start, end, InnerColor);
}
m_cmdInner = VrGizmos.DrawSegments(innerSegs, InnerThickness);
edgeHasChanged = false;
}
}
if (m_cmdInner != null)
VrGizmos.AddDraw(m_cmdInner);
}
}
缝线笔触:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Hologarment.Core
{
/// <summary>
///
/// </summary>namespace Hologarment.Core
public class SewingPen : MonoBehaviour
{
public AnchoredCursor anchoredCursor;
public MeshFilter meshA;
public MeshFilter meshB;
public GameObject currObj;
public List<EdgeHelpers.Edge> gatherA;
public List<EdgeHelpers.Edge> gatherB;
private bool _isReverse;
private bool _isRefresh;
private SewingLine currSewingLine;
//存放经过补充顶点后的edge信息
public readonly struct MyEdge
{
public readonly int _v1;
public readonly List<int> _extra; //补充的点
public readonly int _v2;
public MyEdge(int v1, int v2)
{
this._v1 = v1;
_extra = new List<int>();
this._v2 = v2;
}
public void PushFront(int ex)
{
_extra.Insert(0, ex);
}
public void PushBack(int ex)
{
_extra.Add(ex);
}
}
public List<MyEdge> gatherIndexA;
public List<MyEdge> gatherIndexB;
public List<MyEdge> gatherIndex;
//用于存放缝合顶点索引
public List<int> sewingIndexA;
public List<int> sewingIndexB;
// Start is called before the first frame update
private void Start()
{
Init();
}
private void Init()
{
anchoredCursor.SetCursorActiveTo(false);
meshA = null;
meshB = null;
gatherA = new List<EdgeHelpers.Edge>();
gatherB = new List<EdgeHelpers.Edge>();
gatherIndexA = new List<MyEdge>();
gatherIndexB = new List<MyEdge>();
sewingIndexA = new List<int>();
sewingIndexB = new List<int>();
_isReverse = false;
_isRefresh = false;
currSewingLine = Instantiate<SewingLine>(Resources.Load<SewingLine>("Prefabs/SewingLine"));
currSewingLine.Init();
}
private Ray ray;
RaycastHit hitInfo;
// Update is called once per frame
private void Update()
{
Raycast();
DrawSewingLine();
if (Input.GetKeyDown(KeyCode.R))
{
//逆序重新排列缝线顺序
ReverseSewingPoint();
_isRefresh = true;
}
}
private void OnGUI()
{
GUIStyle style = new GUIStyle();
style.normal.background = null;
style.normal.textColor = Color.white;
style.fontSize = 30;
string text1 = "1.鼠标左键点击面片,确定缝合区间";
string text2 = "2.空格按键对缝合区域进行mesh重构";
string text3 = "3.R键翻转一侧的缝合区域";
GUI.Label(new Rect(30, 30, 200, 30), text1, style);
GUI.Label(new Rect(30, 80, 200, 30), text2, style);
GUI.Label(new Rect(30, 130, 200, 30), text3, style);
}
private void Raycast()
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100);
if (Physics.Raycast(ray, out hitInfo, 100) && anchoredCursor)
{
if (hitInfo.collider.name != "patch")
return;
currObj = hitInfo.collider.gameObject;
//设置目标身上的光标指示器
anchoredCursor.SetCursorActiveTo(true);
anchoredCursor.SetCursorToAnchorPosition(currObj, hitInfo.point); //此处只用于显示光标指示器的位置
if (Input.GetMouseButton(0))
{
//对象A、B不一样
if (meshA == null || currObj == meshA.gameObject)
{
meshA = currObj.GetComponent<MeshFilter>();
}
else if (meshB == null || currObj == meshB.gameObject)
{
meshB = currObj.GetComponent<MeshFilter>();
}
if (currObj == meshA.gameObject)
{
//只翻转第二个对象的edges,所以meshA的edge不需要翻转
var added = anchoredCursor.SetCursorToAnchorPosition(currObj, hitInfo.point, gatherA);
//更新A边上的缝线点索引集合sewingIndexA
if (added)
{
sewingIndexA.Clear();
foreach (var edge in gatherA)
{
sewingIndexA.Add(edge.v1);
}
sewingIndexA.Add(gatherA[gatherA.Count - 1].v2);
}
}
else if (currObj == meshB.gameObject)
{
var added = anchoredCursor.SetCursorToAnchorPosition(currObj, hitInfo.point, gatherB,
_isReverse);
//更新B边上的缝线点索引集合sewingIndexB
if (added)
{
sewingIndexB.Clear();
if (_isReverse)
{
foreach (var edge in gatherB)
{
sewingIndexB.Add(edge.v1);
}
sewingIndexB.Add(gatherB[gatherB.Count - 1].v2);
}
else
{
sewingIndexB.Add(gatherB[0].v1);
foreach (var edge in gatherB)
{
sewingIndexB.Insert(0, edge.v2);
}
}
}
}
}
if (Input.GetMouseButtonDown(1)) //
{
if (currObj == meshA.gameObject)
{
gatherA.Clear();
meshA = null;
}
if (currObj == meshB.gameObject)
{
gatherB.Clear();
meshB = null;
}
}
if (Input.GetMouseButtonUp(0)) //
{
currObj = null;
}
}
else
{
if (anchoredCursor)
{
anchoredCursor.SetCursorActiveTo(false);
}
}
}
private void ReverseSewingPoint()
{
_isReverse = !_isReverse;
//颠倒一个缝线的顺序即可
gatherB.Reverse();
for (int i = 0; i < gatherB.Count; i++)
{
gatherB[i] = new EdgeHelpers.Edge() {v1 = gatherB[i].v2, v2 = gatherB[i].v1};
}
sewingIndexB.Reverse();
}
private void DrawSewingLine()
{
if (meshA == null || meshA.sharedMesh == null)
{
return;
}
//更新A边上的缝线区域
var edgeA = meshA.transform;
var verticesA = meshA.sharedMesh.vertices;
currSewingLine.UpdateSewingEdgeA(edgeA, verticesA, sewingIndexA);
if (meshB == null || meshB.sharedMesh == null)
{
return;
}
//更新B边上的缝线区域
var edgeB = meshB.transform;
var verticesB = meshB.sharedMesh.vertices;
currSewingLine.UpdateSewingEdgeB(edgeB, verticesB, sewingIndexB);
//更新AB边中间的缝线:
currSewingLine.UpdateSewingLines(
edgeA, edgeB,
verticesA, verticesB,
sewingIndexA, sewingIndexB,
_isRefresh
);
if (_isRefresh)
{
_isRefresh = false;
}
}
}
}
按R翻转