• unity---Mesh网格编程(六)


    目录

    1.模型切割

    2.代码


    1.模型切割

    如图,对3D模型的Mesh网格进行切割,会经过若干个三角面。而切割后,将会产生新的面来组成左右两边的物体。

     要记录每个顶点与顶点下标,新的面要顺时针绘制,

     

     

    2.代码

    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. public class CutMode : MonoBehaviour
    4. {
    5. private Material cutMaterial;
    6. private Vector3 _startPos;
    7. private Vector3 _endPos;
    8. private Vector3 _hitPos;
    9. private Vector3 _dir, _upDir, _planeNormal;
    10. private Mesh _mesh;
    11. private Transform _hitTrans;
    12. private MeshFilter _leftMeshFilter;
    13. /// 三角形三个顶点的坐标信息缓存(世界坐标)
    14. private Vector3[] _triangleTemp = new Vector3[3];
    15. /// 三角形三个点乘结果的缓存
    16. private float[] _resultTemp = new float[3];
    17. //左侧(和平面法向量同侧)模型数据
    18. private List _leftVertices = new List();
    19. private List<int> _leftTriangles = new List<int>();
    20. private List _leftNormals = new List();
    21. public List uvs_left;
    22. /// key:原模型顶点下标 value:现模型的顶点下标
    23. private Dictionary<int, int> _leftIndexMapping = new Dictionary<int, int>();
    24. //右侧(和平面法向量反向)模型数据
    25. private List _rightVertices = new List();
    26. private List<int> _rightTriangles = new List<int>();
    27. private List _rightNormals = new List();
    28. public List uvs_right;
    29. /// key:原模型顶点下标 value:现模型的顶点下标
    30. private Dictionary<int, int> _rightIndexMapping = new Dictionary<int, int>();
    31. /// 切面上新生成的顶点
    32. private List _rectionVertexs = new List();
    33. private void Update()
    34. {
    35. if (Input.GetMouseButtonDown(0))
    36. {
    37. _startPos = Input.mousePosition;
    38. }
    39. if (Input.GetMouseButtonUp(0))
    40. {
    41. _endPos = Input.mousePosition;
    42. Ray();
    43. }
    44. }
    45. private void Ray()
    46. {
    47. //两点间的中心点
    48. var center = (_endPos + _startPos) * 0.5f;
    49. var ray = Camera.main.ScreenPointToRay(center);
    50. RaycastHit hit;
    51. if (Physics.Raycast(ray, out hit))
    52. {
    53. _hitTrans = hit.transform;
    54. if (_hitTrans.tag.Equals("cutObj"))
    55. {
    56. Debug.Log("我切到了" + _hitTrans.name);
    57. cutMaterial = _hitTrans.GetComponent().materials[0];
    58. }
    59. else
    60. {
    61. return;
    62. }
    63. _hitPos = hit.point;
    64. _leftMeshFilter = _hitTrans.GetComponent();
    65. _mesh = hit.transform.GetComponent().mesh;
    66. //相机到物体的方向向量 Vector3.normalized归一化向量
    67. _dir = (hit.point - Camera.main.transform.position).normalized;
    68. //垂直于_dir的方向向量 Vector3.Dot(v1,v2)点乘--->计算v1在v2上的投影长度(标量)--法向量
    69. _upDir = (-_dir * Vector3.Dot(Vector3.up, _dir) + Vector3.up).normalized;
    70. //平面 Vector3.Cross叉乘
    71. _planeNormal = Vector3.Cross(_dir, _upDir);
    72. //计算滑动方向与角度
    73. Vector3 sildeDir = _endPos - _startPos;
    74. Vector3 baseDir = sildeDir.y < 0 ? -Vector3.up : Vector3.up;
    75. float angle = Vector3.Angle(sildeDir, baseDir);
    76. if (sildeDir.y < 0)
    77. {
    78. angle = sildeDir.x > 0 ? angle : -angle;
    79. }
    80. else
    81. {
    82. angle = sildeDir.x > 0 ? -angle : angle;
    83. }
    84. //角度转弧度
    85. angle *= Mathf.Deg2Rad;
    86. //sin cos 需传入的参数为弧度
    87. _upDir = _upDir * Mathf.Cos(angle) + _planeNormal * Mathf.Sin(angle);
    88. _planeNormal = Vector3.Cross(_dir, _upDir);
    89. Cut();
    90. }
    91. else
    92. {
    93. _hitPos = Vector3.zero;
    94. _mesh = null;
    95. }
    96. }
    97. private void Cut()
    98. {
    99. if (_mesh == null)
    100. return;
    101. ClearData();
    102. CalculateVertexInfo();
    103. GenerateSectionInfo();
    104. GenerateMesh();
    105. }
    106. private void ClearData()
    107. {
    108. _leftVertices.Clear();
    109. _leftTriangles.Clear();
    110. _leftNormals.Clear();
    111. _leftIndexMapping.Clear();
    112. _rightNormals.Clear();
    113. _rightTriangles.Clear();
    114. _rightVertices.Clear();
    115. _rightIndexMapping.Clear();
    116. _rectionVertexs.Clear();
    117. }
    118. private void GenerateMesh()
    119. {
    120. GenerateLeftMesh();
    121. GenerateRightMesh();
    122. }
    123. private void GenerateLeftMesh()
    124. {
    125. Mesh mesh = new Mesh();
    126. mesh.name = "leftMesh";
    127. mesh.vertices = _leftVertices.ToArray();
    128. mesh.triangles = _leftTriangles.ToArray();
    129. mesh.normals = _leftNormals.ToArray();
    130. //ToDo mesh.uv =
    131. _leftMeshFilter.mesh = mesh;
    132. }
    133. private int newObjNum = 0;
    134. private void GenerateRightMesh()
    135. {
    136. Mesh mesh = new Mesh();
    137. mesh.name = "rightMesh";
    138. mesh.vertices = _rightVertices.ToArray();
    139. mesh.normals = _rightNormals.ToArray();
    140. mesh.triangles = _rightTriangles.ToArray();
    141. //ToDo mesh.uv =
    142. GameObject newGo = new GameObject();
    143. newGo.name = "newObj" + newObjNum;
    144. newGo.transform.tag = "cutObj";
    145. newGo.transform.position = _hitTrans.position;
    146. newGo.transform.rotation = _hitTrans.rotation;
    147. newGo.AddComponent().mesh = mesh;
    148. //newGo.AddComponent().material = _hitTrans.GetComponent().material;
    149. newGo.AddComponent().material = cutMaterial;
    150. newGo.AddComponent();
    151. newGo.AddComponent().convex = true;
    152. newObjNum++;
    153. }
    154. ///
    155. /// 分别计算并存储切开的两个部分的顶点信息
    156. ///
    157. private void CalculateVertexInfo()
    158. {
    159. var triangles = _mesh.triangles;
    160. for (int i = 0; i < triangles.Length; i += 3)
    161. {
    162. //三个顶点在原triangles中下标是 i i+1 i+2
    163. GetDotResult(i, triangles);
    164. if (_resultTemp[0] >= 0 && _resultTemp[1] >= 0 && _resultTemp[2] >= 0)
    165. {
    166. //左侧
    167. SaveOldVertex(i, true);
    168. }
    169. else if (_resultTemp[0] <= 0 && _resultTemp[1] <= 0 && _resultTemp[2] <= 0)
    170. {
    171. //右侧
    172. SaveOldVertex(i, false);
    173. }
    174. else
    175. {
    176. //被切割的三角形部分
    177. int differentIndex = GetDifferentSidePointIndex();
    178. //当前点在triangles的下标
    179. int p0_Index = i + differentIndex;
    180. int p1_index = (differentIndex + 1) % 3 + i;
    181. //先算出c1点进行存储
    182. SavePointOnSection(_mesh.triangles[p0_Index], _mesh.triangles[p1_index]);
    183. int p2_index = (differentIndex + 2) % 3 + i;
    184. //再算出c2点进行存储
    185. SavePointOnSection(_mesh.triangles[p0_Index], _mesh.triangles[p2_index]);
    186. SaveCutTriangleVertex(_resultTemp[differentIndex], p0_Index, p1_index, p2_index);
    187. }
    188. }
    189. }
    190. private void SaveCutTriangleVertex(float result, int p0, int p1, int p2)
    191. {
    192. if (result >= 0)
    193. {
    194. SaveOldVertex(p0, _leftVertices, _leftNormals, _leftIndexMapping);
    195. SaveSectionVertexWithOnePoint(p0, _leftTriangles, _leftVertices, _leftNormals, _leftIndexMapping);
    196. SaveOldVertex(p1, _rightVertices, _rightNormals, _rightIndexMapping);
    197. SaveOldVertex(p2, _rightVertices, _rightNormals, _rightIndexMapping);
    198. SaveSectionVertexWithTwoPoint(p1, p2, _rightTriangles, _rightVertices, _rightNormals, _rightIndexMapping);
    199. }
    200. else
    201. {
    202. SaveOldVertex(p0, _rightVertices, _rightNormals, _rightIndexMapping);
    203. SaveSectionVertexWithOnePoint(p0, _rightTriangles, _rightVertices, _rightNormals, _rightIndexMapping);
    204. SaveOldVertex(p1, _leftVertices, _leftNormals, _leftIndexMapping);
    205. SaveOldVertex(p2, _leftVertices, _leftNormals, _leftIndexMapping);
    206. SaveSectionVertexWithTwoPoint(p1, p2, _leftTriangles, _leftVertices, _leftNormals, _leftIndexMapping);
    207. }
    208. }
    209. private void SaveSectionVertexWithOnePoint(
    210. int index,
    211. List<int> curTriangles,
    212. List curVertices,
    213. List curNormals,
    214. Dictionary<int, int> indexMapping)
    215. {
    216. int vertexIndex = _mesh.triangles[index];
    217. //存储c1
    218. curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 2]);
    219. //存储c2
    220. curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 1]);
    221. curNormals.Add(_mesh.normals[vertexIndex]);
    222. curNormals.Add(_mesh.normals[vertexIndex]);
    223. curTriangles.Add(indexMapping[vertexIndex]);
    224. curTriangles.Add(curVertices.Count - 2);
    225. curTriangles.Add(curVertices.Count - 1);
    226. }
    227. private void SaveSectionVertexWithTwoPoint(
    228. int index1,
    229. int index2,
    230. List<int> curTriangles,
    231. List curVertices,
    232. List curNormals,
    233. Dictionary<int, int> indexMapping)
    234. {
    235. int vertexIndex1 = _mesh.triangles[index1];
    236. int vertexIndex2 = _mesh.triangles[index2];
    237. //存储c1
    238. curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 2]);
    239. //存储c2
    240. curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 1]);
    241. curNormals.Add(_mesh.normals[vertexIndex1]);
    242. curNormals.Add(_mesh.normals[vertexIndex2]);
    243. //c1-p1-p2
    244. curTriangles.Add(curVertices.Count - 2);
    245. curTriangles.Add(indexMapping[vertexIndex1]);
    246. curTriangles.Add(indexMapping[vertexIndex2]);
    247. //p2-c2-c1
    248. curTriangles.Add(indexMapping[vertexIndex2]);
    249. curTriangles.Add(curVertices.Count - 1);
    250. curTriangles.Add(curVertices.Count - 2);
    251. }
    252. ///
    253. /// 返回值是对应点在_resultTemp中的下标
    254. ///
    255. private int GetDifferentSidePointIndex()
    256. {
    257. List<int> temp1 = new List<int>(2);
    258. List<int> temp2 = new List<int>(2);
    259. for (int i = 0; i < _resultTemp.Length; i++)
    260. {
    261. if (_resultTemp[i] > 0)
    262. {
    263. temp1.Add(i);
    264. }
    265. else
    266. {
    267. temp2.Add(i);
    268. }
    269. }
    270. if (temp1.Count == 1)
    271. {
    272. return temp1[0];
    273. }
    274. else
    275. {
    276. return temp2[0];
    277. }
    278. }
    279. //参数是 原模型vertices下标
    280. private void SavePointOnSection(int index1, int index2)
    281. {
    282. Vector3 side = _mesh.vertices[index2] - _mesh.vertices[index1];
    283. //方向向量 --- 本地坐标系转世界坐标系
    284. Vector3 dir = _hitTrans.TransformDirection(side.normalized);
    285. Vector3 startPos = _hitTrans.TransformPoint(_mesh.vertices[index1]);
    286. float lengthOnNormal = Vector3.Dot(_hitPos, _planeNormal) - Vector3.Dot(startPos, _planeNormal);
    287. float length = lengthOnNormal / Vector3.Dot(dir, _planeNormal);
    288. Vector3 target = startPos + dir * length;
    289. _rectionVertexs.Add(_hitTrans.InverseTransformPoint(target));
    290. }
    291. private void GetDotResult(int index, int[] triangles)
    292. {
    293. for (int i = 0; i < _triangleTemp.Length; i++)
    294. {
    295. _triangleTemp[i] = _hitTrans.TransformPoint(_mesh.vertices[triangles[index + i]]);
    296. _resultTemp[i] = Vector3.Dot(_planeNormal, _triangleTemp[i] - _hitPos);
    297. }
    298. }
    299. private void SaveOldVertex(int index, bool isLeft)
    300. {
    301. if (isLeft)
    302. {
    303. SaveTriangleVertex(index, _leftTriangles, _leftVertices, _leftNormals, _leftIndexMapping);
    304. }
    305. else
    306. {
    307. SaveTriangleVertex(index, _rightTriangles, _rightVertices, _rightNormals, _rightIndexMapping);
    308. }
    309. }
    310. private void SaveTriangleVertex(
    311. int index,
    312. List<int> curTriangles,
    313. List curVertices,
    314. List curNormals,
    315. Dictionary<int, int> indexMapping)
    316. {
    317. for (int i = 0; i < 3; i++)
    318. {
    319. SaveOldVertex(index + i, curVertices, curNormals, indexMapping);
    320. curTriangles.Add(indexMapping[_mesh.triangles[index + i]]);
    321. }
    322. }
    323. private void SaveOldVertex(
    324. int index,
    325. List curVertices,
    326. List curNormals,
    327. Dictionary<int, int> indexMapping)
    328. {
    329. int vertexIndex = _mesh.triangles[index];
    330. if (!indexMapping.ContainsKey(vertexIndex))
    331. {
    332. curVertices.Add(_mesh.vertices[vertexIndex]);
    333. curNormals.Add(_mesh.normals[vertexIndex]);
    334. indexMapping.Add(vertexIndex, curVertices.Count - 1);
    335. }
    336. }
    337. //生成切面信息
    338. private void GenerateSectionInfo()
    339. {
    340. Vector3 center = (_rectionVertexs[0] + _rectionVertexs[_rectionVertexs.Count / 2]) * 0.5f;
    341. Vector3 centerNormal = _hitTrans.InverseTransformDirection(_planeNormal);
    342. SaveSectionCenter(center, centerNormal);
    343. int leftCenterIndex = _leftVertices.Count - 1;
    344. int rightCenterIndex = _rightVertices.Count - 1;
    345. for (int i = 0; i < _rectionVertexs.Count; i += 2)
    346. {
    347. Vector3 v1 = _rectionVertexs[i];
    348. Vector3 v2 = _rectionVertexs[i + 1];
    349. Vector3 normal = Vector3.Cross(v1 - center, v2 - center);
    350. SaveSectionVertexInfo(i, -centerNormal, _leftVertices, _leftNormals);
    351. SaveLeftSectionTriangle(_planeNormal, normal, leftCenterIndex, _leftTriangles, _leftVertices);
    352. SaveSectionVertexInfo(i, centerNormal, _rightVertices, _rightNormals);
    353. SaveRightSectionTriangle(_planeNormal, normal, rightCenterIndex, _rightTriangles, _rightVertices);
    354. }
    355. }
    356. private void SaveLeftSectionTriangle(Vector3 planeNormal, Vector3 normal, int centerIndex, List<int> triangles, List vertices)
    357. {
    358. if (Vector3.Dot(planeNormal, normal) < 0)
    359. {
    360. //左侧切面 三角形法向量方向和planeNormal方向相反,才能正常显示
    361. // 0 1 2
    362. triangles.Add(centerIndex);
    363. triangles.Add(vertices.Count - 2);
    364. triangles.Add(vertices.Count - 1);
    365. }
    366. else
    367. {
    368. // 0 2 1
    369. triangles.Add(centerIndex);
    370. triangles.Add(vertices.Count - 1);
    371. triangles.Add(vertices.Count - 2);
    372. }
    373. }
    374. private void SaveRightSectionTriangle(Vector3 planeNormal, Vector3 normal, int centerIndex, List<int> triangles, List vertices)
    375. {
    376. if (Vector3.Dot(planeNormal, normal) > 0)
    377. {
    378. //右侧切面 三角形法向量方向和planeNormal方向相同,才能正常显示
    379. // 0 1 2
    380. triangles.Add(centerIndex);
    381. triangles.Add(vertices.Count - 2);
    382. triangles.Add(vertices.Count - 1);
    383. }
    384. else
    385. {
    386. // 0 2 1
    387. triangles.Add(centerIndex);
    388. triangles.Add(vertices.Count - 1);
    389. triangles.Add(vertices.Count - 2);
    390. }
    391. }
    392. private void SaveSectionVertexInfo(int index, Vector3 normal, List vertices, List normals)
    393. {
    394. vertices.Add(_rectionVertexs[index]);
    395. vertices.Add(_rectionVertexs[index + 1]);
    396. normals.Add(normal);
    397. normals.Add(normal);
    398. }
    399. private void SaveSectionCenter(Vector3 center, Vector3 normal)
    400. {
    401. _leftVertices.Add(center);
    402. _leftNormals.Add(-normal);
    403. _rightVertices.Add(center);
    404. _rightNormals.Add(normal);
    405. }
    406. }

  • 相关阅读:
    弱引用回调引发的坑
    docker 容器和镜像的备份
    【色彩管理】锐印添加ICC曲线教程
    云原生技术 --- k8s节点组件之kube-proxy的学习与理解
    反超 PowerDesigner,这个国产数据库建模工具很强
    使用Dapr和Tye启动服务
    小程序中如何给会员卡设置到期时间
    高等数学(第七版)同济大学 习题5-2 个人解答
    聚观早报 | 特斯拉发布赛博啤酒套装;小米汽车售价曝光
    MySQL 用户授权管理及白名单
  • 原文地址:https://blog.csdn.net/lalate/article/details/128132328