FastHull.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. // Shatter Toolkit
  2. // Copyright 2015 Gustav Olsson
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace ShatterToolkit
  6. {
  7. public class FastHull : IHull
  8. {
  9. protected static float smallestValidLength = 0.01f;
  10. protected static float smallestValidRatio = 0.05f;
  11. protected bool isValid = true;
  12. protected List<Vector3> vertices;
  13. protected List<Vector3> normals;
  14. protected List<Color32> colors;
  15. protected List<Vector4> tangents;
  16. protected List<Vector2> uvs;
  17. protected List<int> indices;
  18. public FastHull(Mesh mesh)
  19. {
  20. vertices = new List<Vector3>(mesh.vertices);
  21. indices = new List<int>(mesh.triangles);
  22. if (mesh.normals.Length > 0)
  23. {
  24. normals = new List<Vector3>(mesh.normals);
  25. }
  26. if (mesh.colors32.Length > 0)
  27. {
  28. colors = new List<Color32>(mesh.colors32);
  29. }
  30. if (mesh.tangents.Length > 0)
  31. {
  32. tangents = new List<Vector4>(mesh.tangents);
  33. }
  34. if (mesh.uv.Length > 0)
  35. {
  36. uvs = new List<Vector2>(mesh.uv);
  37. }
  38. }
  39. public FastHull(FastHull reference)
  40. {
  41. vertices = new List<Vector3>(reference.vertices.Count);
  42. indices = new List<int>(reference.indices.Count);
  43. if (reference.normals != null)
  44. {
  45. normals = new List<Vector3>(reference.normals.Count);
  46. }
  47. if (reference.colors != null)
  48. {
  49. colors = new List<Color32>(reference.colors.Count);
  50. }
  51. if (reference.tangents != null)
  52. {
  53. tangents = new List<Vector4>(reference.tangents.Count);
  54. }
  55. if (reference.uvs != null)
  56. {
  57. uvs = new List<Vector2>(reference.uvs.Count);
  58. }
  59. }
  60. public bool IsEmpty
  61. {
  62. get { return !isValid || vertices.Count < 3 || indices.Count < 3; }
  63. }
  64. public Mesh GetMesh()
  65. {
  66. if (isValid)
  67. {
  68. Mesh mesh = new Mesh();
  69. // Required properties
  70. mesh.vertices = vertices.ToArray();
  71. mesh.triangles = indices.ToArray();
  72. // Optional properties
  73. if (normals != null)
  74. {
  75. mesh.normals = normals.ToArray();
  76. }
  77. if (colors != null)
  78. {
  79. mesh.colors32 = colors.ToArray();
  80. }
  81. if (tangents != null)
  82. {
  83. mesh.tangents = tangents.ToArray();
  84. }
  85. if (uvs != null)
  86. {
  87. mesh.uv = uvs.ToArray();
  88. }
  89. return mesh;
  90. }
  91. return null;
  92. }
  93. public void Split(Vector3 localPointOnPlane, Vector3 localPlaneNormal, bool fillCut, UvMapper uvMapper, ColorMapper colorMapper, out IHull resultA, out IHull resultB)
  94. {
  95. if (localPlaneNormal == Vector3.zero)
  96. {
  97. localPlaneNormal = Vector3.up;
  98. }
  99. FastHull a = new FastHull(this);
  100. FastHull b = new FastHull(this);
  101. bool[] vertexAbovePlane;
  102. int[] oldToNewVertexMap;
  103. AssignVertices(a, b, localPointOnPlane, localPlaneNormal, out vertexAbovePlane, out oldToNewVertexMap);
  104. IList<Vector3> cutEdges;
  105. AssignTriangles(a, b, vertexAbovePlane, oldToNewVertexMap, localPointOnPlane, localPlaneNormal, out cutEdges);
  106. if (fillCut)
  107. {
  108. if (colors != null && colorMapper == null)
  109. {
  110. Debug.LogWarning("Fill cut failed: A ColorMapper was not provided even though the mesh has a color channel");
  111. }
  112. else if ((tangents != null || uvs != null) && uvMapper == null)
  113. {
  114. Debug.LogWarning("Fill cut failed: A UvMapper was not provided even though the mesh has a tangent/uv channel");
  115. }
  116. else
  117. {
  118. FillCutEdges(a, b, cutEdges, localPlaneNormal, uvMapper, colorMapper);
  119. }
  120. }
  121. ValidateOutput(a, b, localPlaneNormal);
  122. // Set output
  123. resultA = a;
  124. resultB = b;
  125. }
  126. protected void AssignVertices(FastHull a, FastHull b, Vector3 pointOnPlane, Vector3 planeNormal, out bool[] vertexAbovePlane, out int[] oldToNewVertexMap)
  127. {
  128. vertexAbovePlane = new bool[vertices.Count];
  129. oldToNewVertexMap = new int[vertices.Count];
  130. for (int i = 0; i < vertices.Count; i++)
  131. {
  132. Vector3 vertex = vertices[i];
  133. bool abovePlane = Vector3.Dot(vertex - pointOnPlane, planeNormal) >= 0.0f;
  134. vertexAbovePlane[i] = abovePlane;
  135. if (abovePlane)
  136. {
  137. // Assign vertex to hull A
  138. oldToNewVertexMap[i] = a.vertices.Count;
  139. a.vertices.Add(vertex);
  140. if (normals != null)
  141. {
  142. a.normals.Add(normals[i]);
  143. }
  144. if (colors != null)
  145. {
  146. a.colors.Add(colors[i]);
  147. }
  148. if (tangents != null)
  149. {
  150. a.tangents.Add(tangents[i]);
  151. }
  152. if (uvs != null)
  153. {
  154. a.uvs.Add(uvs[i]);
  155. }
  156. }
  157. else
  158. {
  159. // Assign vertex to hull B
  160. oldToNewVertexMap[i] = b.vertices.Count;
  161. b.vertices.Add(vertex);
  162. if (normals != null)
  163. {
  164. b.normals.Add(normals[i]);
  165. }
  166. if (colors != null)
  167. {
  168. b.colors.Add(colors[i]);
  169. }
  170. if (tangents != null)
  171. {
  172. b.tangents.Add(tangents[i]);
  173. }
  174. if (uvs != null)
  175. {
  176. b.uvs.Add(uvs[i]);
  177. }
  178. }
  179. }
  180. }
  181. protected void AssignTriangles(FastHull a, FastHull b, bool[] vertexAbovePlane, int[] oldToNewVertexMap, Vector3 pointOnPlane, Vector3 planeNormal, out IList<Vector3> cutEdges)
  182. {
  183. cutEdges = new List<Vector3>();
  184. int triangleCount = indices.Count / 3;
  185. for (int i = 0; i < triangleCount; i++)
  186. {
  187. int index0 = indices[i * 3 + 0];
  188. int index1 = indices[i * 3 + 1];
  189. int index2 = indices[i * 3 + 2];
  190. bool above0 = vertexAbovePlane[index0];
  191. bool above1 = vertexAbovePlane[index1];
  192. bool above2 = vertexAbovePlane[index2];
  193. if (above0 && above1 && above2)
  194. {
  195. // Assign triangle to hull A
  196. a.indices.Add(oldToNewVertexMap[index0]);
  197. a.indices.Add(oldToNewVertexMap[index1]);
  198. a.indices.Add(oldToNewVertexMap[index2]);
  199. }
  200. else if (!above0 && !above1 && !above2)
  201. {
  202. // Assign triangle to hull B
  203. b.indices.Add(oldToNewVertexMap[index0]);
  204. b.indices.Add(oldToNewVertexMap[index1]);
  205. b.indices.Add(oldToNewVertexMap[index2]);
  206. }
  207. else
  208. {
  209. // Split triangle
  210. int top, cw, ccw;
  211. if (above1 == above2 && above0 != above1)
  212. {
  213. top = index0;
  214. cw = index1;
  215. ccw = index2;
  216. }
  217. else if (above2 == above0 && above1 != above2)
  218. {
  219. top = index1;
  220. cw = index2;
  221. ccw = index0;
  222. }
  223. else
  224. {
  225. top = index2;
  226. cw = index0;
  227. ccw = index1;
  228. }
  229. Vector3 cutVertex0, cutVertex1;
  230. if (vertexAbovePlane[top])
  231. {
  232. SplitTriangle(a, b, oldToNewVertexMap, pointOnPlane, planeNormal, top, cw, ccw, out cutVertex0, out cutVertex1);
  233. }
  234. else
  235. {
  236. SplitTriangle(b, a, oldToNewVertexMap, pointOnPlane, planeNormal, top, cw, ccw, out cutVertex1, out cutVertex0);
  237. }
  238. // Add cut edge
  239. if (cutVertex0 != cutVertex1)
  240. {
  241. cutEdges.Add(cutVertex0);
  242. cutEdges.Add(cutVertex1);
  243. }
  244. }
  245. }
  246. }
  247. protected void SplitTriangle(FastHull topHull, FastHull bottomHull, int[] oldToNewVertexMap, Vector3 pointOnPlane, Vector3 planeNormal, int top, int cw, int ccw, out Vector3 cwIntersection, out Vector3 ccwIntersection)
  248. {
  249. Vector3 v0 = vertices[top];
  250. Vector3 v1 = vertices[cw];
  251. Vector3 v2 = vertices[ccw];
  252. // Intersect the top-cw edge with the plane
  253. float cwDenominator = Vector3.Dot(v1 - v0, planeNormal);
  254. float cwScalar = Mathf.Clamp01(Vector3.Dot(pointOnPlane - v0, planeNormal) / cwDenominator);
  255. // Intersect the top-ccw edge with the plane
  256. float ccwDenominator = Vector3.Dot(v2 - v0, planeNormal);
  257. float ccwScalar = Mathf.Clamp01(Vector3.Dot(pointOnPlane - v0, planeNormal) / ccwDenominator);
  258. // Interpolate vertex positions
  259. Vector3 cwVertex = new Vector3();
  260. cwVertex.x = v0.x + (v1.x - v0.x) * cwScalar;
  261. cwVertex.y = v0.y + (v1.y - v0.y) * cwScalar;
  262. cwVertex.z = v0.z + (v1.z - v0.z) * cwScalar;
  263. Vector3 ccwVertex = new Vector3();
  264. ccwVertex.x = v0.x + (v2.x - v0.x) * ccwScalar;
  265. ccwVertex.y = v0.y + (v2.y - v0.y) * ccwScalar;
  266. ccwVertex.z = v0.z + (v2.z - v0.z) * ccwScalar;
  267. // Create top triangle
  268. int cwA = topHull.vertices.Count;
  269. topHull.vertices.Add(cwVertex);
  270. int ccwA = topHull.vertices.Count;
  271. topHull.vertices.Add(ccwVertex);
  272. topHull.indices.Add(oldToNewVertexMap[top]);
  273. topHull.indices.Add(cwA);
  274. topHull.indices.Add(ccwA);
  275. // Create bottom triangles
  276. int cwB = bottomHull.vertices.Count;
  277. bottomHull.vertices.Add(cwVertex);
  278. int ccwB = bottomHull.vertices.Count;
  279. bottomHull.vertices.Add(ccwVertex);
  280. bottomHull.indices.Add(oldToNewVertexMap[cw]);
  281. bottomHull.indices.Add(oldToNewVertexMap[ccw]);
  282. bottomHull.indices.Add(ccwB);
  283. bottomHull.indices.Add(oldToNewVertexMap[cw]);
  284. bottomHull.indices.Add(ccwB);
  285. bottomHull.indices.Add(cwB);
  286. // Interpolate normals
  287. if (normals != null)
  288. {
  289. Vector3 n0 = normals[top];
  290. Vector3 n1 = normals[cw];
  291. Vector3 n2 = normals[ccw];
  292. Vector3 cwNormal = new Vector3();
  293. cwNormal.x = n0.x + (n1.x - n0.x) * cwScalar;
  294. cwNormal.y = n0.y + (n1.y - n0.y) * cwScalar;
  295. cwNormal.z = n0.z + (n1.z - n0.z) * cwScalar;
  296. cwNormal.Normalize();
  297. Vector3 ccwNormal = new Vector3();
  298. ccwNormal.x = n0.x + (n2.x - n0.x) * ccwScalar;
  299. ccwNormal.y = n0.y + (n2.y - n0.y) * ccwScalar;
  300. ccwNormal.z = n0.z + (n2.z - n0.z) * ccwScalar;
  301. ccwNormal.Normalize();
  302. // Add vertex property
  303. topHull.normals.Add(cwNormal);
  304. topHull.normals.Add(ccwNormal);
  305. bottomHull.normals.Add(cwNormal);
  306. bottomHull.normals.Add(ccwNormal);
  307. }
  308. // Interpolate colors
  309. if (colors != null)
  310. {
  311. Color32 c0 = colors[top];
  312. Color32 c1 = colors[cw];
  313. Color32 c2 = colors[ccw];
  314. Color32 cwColor = Color32.Lerp(c0, c1, cwScalar);
  315. Color32 ccwColor = Color32.Lerp(c0, c2, ccwScalar);
  316. // Add vertex property
  317. topHull.colors.Add(cwColor);
  318. topHull.colors.Add(ccwColor);
  319. bottomHull.colors.Add(cwColor);
  320. bottomHull.colors.Add(ccwColor);
  321. }
  322. // Interpolate tangents
  323. if (tangents != null)
  324. {
  325. Vector4 t0 = tangents[top];
  326. Vector4 t1 = tangents[cw];
  327. Vector4 t2 = tangents[ccw];
  328. Vector4 cwTangent = new Vector4();
  329. cwTangent.x = t0.x + (t1.x - t0.x) * cwScalar;
  330. cwTangent.y = t0.y + (t1.y - t0.y) * cwScalar;
  331. cwTangent.z = t0.z + (t1.z - t0.z) * cwScalar;
  332. cwTangent.Normalize();
  333. cwTangent.w = t1.w;
  334. Vector4 ccwTangent = new Vector4();
  335. ccwTangent.x = t0.x + (t2.x - t0.x) * ccwScalar;
  336. ccwTangent.y = t0.y + (t2.y - t0.y) * ccwScalar;
  337. ccwTangent.z = t0.z + (t2.z - t0.z) * ccwScalar;
  338. ccwTangent.Normalize();
  339. ccwTangent.w = t2.w;
  340. // Add vertex property
  341. topHull.tangents.Add(cwTangent);
  342. topHull.tangents.Add(ccwTangent);
  343. bottomHull.tangents.Add(cwTangent);
  344. bottomHull.tangents.Add(ccwTangent);
  345. }
  346. // Interpolate uvs
  347. if (uvs != null)
  348. {
  349. Vector2 u0 = uvs[top];
  350. Vector2 u1 = uvs[cw];
  351. Vector2 u2 = uvs[ccw];
  352. Vector2 cwUv = new Vector2();
  353. cwUv.x = u0.x + (u1.x - u0.x) * cwScalar;
  354. cwUv.y = u0.y + (u1.y - u0.y) * cwScalar;
  355. Vector2 ccwUv = new Vector2();
  356. ccwUv.x = u0.x + (u2.x - u0.x) * ccwScalar;
  357. ccwUv.y = u0.y + (u2.y - u0.y) * ccwScalar;
  358. // Add vertex property
  359. topHull.uvs.Add(cwUv);
  360. topHull.uvs.Add(ccwUv);
  361. bottomHull.uvs.Add(cwUv);
  362. bottomHull.uvs.Add(ccwUv);
  363. }
  364. // Set output
  365. cwIntersection = cwVertex;
  366. ccwIntersection = ccwVertex;
  367. }
  368. protected void FillCutEdges(FastHull a, FastHull b, IList<Vector3> edges, Vector3 planeNormal, UvMapper uvMapper, ColorMapper colorMapper)
  369. {
  370. int edgeCount = edges.Count / 2;
  371. List<Vector3> points = new List<Vector3>(edgeCount);
  372. List<int> outline = new List<int>(edgeCount * 2);
  373. int start = 0;
  374. for (int current = 0; current < edgeCount; current++)
  375. {
  376. int next = current + 1;
  377. // Find the next edge
  378. int nearest = start;
  379. float nearestDistance = (edges[current * 2 + 1] - edges[start * 2 + 0]).sqrMagnitude;
  380. for (int other = next; other < edgeCount; other++)
  381. {
  382. float distance = (edges[current * 2 + 1] - edges[other * 2 + 0]).sqrMagnitude;
  383. if (distance < nearestDistance)
  384. {
  385. nearest = other;
  386. nearestDistance = distance;
  387. }
  388. }
  389. // Is the current edge the last edge in this edge loop?
  390. if (nearest == start && current > start)
  391. {
  392. int pointStart = points.Count;
  393. int pointCounter = pointStart;
  394. // Add this edge loop to the triangulation lists
  395. for (int edge = start; edge < current; edge++)
  396. {
  397. points.Add(edges[edge * 2 + 0]);
  398. outline.Add(pointCounter++);
  399. outline.Add(pointCounter);
  400. }
  401. points.Add(edges[current * 2 + 0]);
  402. outline.Add(pointCounter);
  403. outline.Add(pointStart);
  404. // Start a new edge loop
  405. start = next;
  406. }
  407. else if (next < edgeCount)
  408. {
  409. // Move the nearest edge so that it follows the current edge
  410. Vector3 n0 = edges[next * 2 + 0];
  411. Vector3 n1 = edges[next * 2 + 1];
  412. edges[next * 2 + 0] = edges[nearest * 2 + 0];
  413. edges[next * 2 + 1] = edges[nearest * 2 + 1];
  414. edges[nearest * 2 + 0] = n0;
  415. edges[nearest * 2 + 1] = n1;
  416. }
  417. }
  418. if (points.Count > 0)
  419. {
  420. // Triangulate the outline
  421. int[] newEdges, newTriangles, newTriangleEdges;
  422. ITriangulator triangulator = new Triangulator(points, outline, planeNormal);
  423. triangulator.Fill(out newEdges, out newTriangles, out newTriangleEdges);
  424. // Add the new vertices
  425. int offsetA = a.vertices.Count;
  426. int offsetB = b.vertices.Count;
  427. a.vertices.AddRange(points);
  428. b.vertices.AddRange(points);
  429. if (normals != null)
  430. {
  431. Vector3 normalA = -planeNormal;
  432. Vector3 normalB = planeNormal;
  433. for (int i = 0; i < points.Count; i++)
  434. {
  435. a.normals.Add(normalA);
  436. b.normals.Add(normalB);
  437. }
  438. }
  439. if (colors != null)
  440. {
  441. Color32[] colorsA, colorsB;
  442. colorMapper.Map(points, planeNormal, out colorsA, out colorsB);
  443. a.colors.AddRange(colorsA);
  444. b.colors.AddRange(colorsB);
  445. }
  446. if (tangents != null || uvs != null)
  447. {
  448. Vector4[] tangentsA, tangentsB;
  449. Vector2[] uvsA, uvsB;
  450. uvMapper.Map(points, planeNormal, out tangentsA, out tangentsB, out uvsA, out uvsB);
  451. if (tangents != null)
  452. {
  453. a.tangents.AddRange(tangentsA);
  454. b.tangents.AddRange(tangentsB);
  455. }
  456. if (uvs != null)
  457. {
  458. a.uvs.AddRange(uvsA);
  459. b.uvs.AddRange(uvsB);
  460. }
  461. }
  462. // Add the new triangles
  463. int newTriangleCount = newTriangles.Length / 3;
  464. for (int i = 0; i < newTriangleCount; i++)
  465. {
  466. a.indices.Add(offsetA + newTriangles[i * 3 + 0]);
  467. a.indices.Add(offsetA + newTriangles[i * 3 + 2]);
  468. a.indices.Add(offsetA + newTriangles[i * 3 + 1]);
  469. b.indices.Add(offsetB + newTriangles[i * 3 + 0]);
  470. b.indices.Add(offsetB + newTriangles[i * 3 + 1]);
  471. b.indices.Add(offsetB + newTriangles[i * 3 + 2]);
  472. }
  473. }
  474. }
  475. protected void ValidateOutput(FastHull a, FastHull b, Vector3 planeNormal)
  476. {
  477. float lengthA = a.LengthAlongAxis(planeNormal);
  478. float lengthB = b.LengthAlongAxis(planeNormal);
  479. float sum = lengthA + lengthB;
  480. if (sum < smallestValidLength)
  481. {
  482. a.isValid = false;
  483. b.isValid = false;
  484. }
  485. else if (lengthA / sum < smallestValidRatio)
  486. {
  487. a.isValid = false;
  488. }
  489. else if (lengthB / sum < smallestValidRatio)
  490. {
  491. b.isValid = false;
  492. }
  493. }
  494. protected float LengthAlongAxis(Vector3 axis)
  495. {
  496. if (vertices.Count > 0)
  497. {
  498. float min = Vector3.Dot(vertices[0], axis);
  499. float max = min;
  500. foreach (Vector3 vertex in vertices)
  501. {
  502. float distance = Vector3.Dot(vertex, axis);
  503. min = Mathf.Min(distance, min);
  504. max = Mathf.Max(distance, max);
  505. }
  506. return max - min;
  507. }
  508. return 0.0f;
  509. }
  510. }
  511. }