MeshSmoother.cs 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Threading.Tasks;
  4. using UnityEngine;
  5. namespace FlatKit {
  6. public static class MeshSmoother {
  7. private const int SmoothNormalUVChannel = 2;
  8. /// <summary>
  9. /// Performs normal smoothing on the current mesh filter associated with this component asynchronously.
  10. /// This method will not try and re-smooth meshes which have already been smoothed.
  11. /// </summary>
  12. /// <returns>A task which will complete once normal smoothing is finished.</returns>
  13. public static Task SmoothNormalsA(Mesh mesh) {
  14. // Create a copy of the vertices and normals and apply the smoothing in an async task.
  15. var vertices = mesh.vertices;
  16. var normals = mesh.normals;
  17. var asyncTask = Task.Run(() => CalculateSmoothNormals(vertices, normals));
  18. // Once the async task is complete, apply the smoothed normals to the mesh on the main thread.
  19. return asyncTask.ContinueWith(i => { mesh.SetUVs(SmoothNormalUVChannel, i.Result); },
  20. TaskScheduler.FromCurrentSynchronizationContext());
  21. }
  22. public static void SmoothNormals(Mesh mesh) {
  23. var result = CalculateSmoothNormals(mesh.vertices, mesh.normals);
  24. mesh.SetUVs(SmoothNormalUVChannel, result);
  25. }
  26. public static void ClearNormalsUV(Mesh mesh) {
  27. mesh.SetUVs(SmoothNormalUVChannel, (Vector3[])null);
  28. }
  29. public static bool HasSmoothNormals(Mesh mesh) {
  30. return mesh.uv3 != null && mesh.uv3.Length > 0;
  31. }
  32. /// <summary>
  33. /// This method groups vertices in a mesh that share the same location in space then averages the normals of those vertices.
  34. /// For example, if you imagine the 3 vertices that make up one corner of a cube. Normally there will be 3 normals facing in the direction
  35. /// of each face that touches that corner. This method will take those 3 normals and average them into a normal that points in the
  36. /// direction from the center of the cube to the corner of the cube.
  37. /// </summary>
  38. /// <param name="vertices">A list of vertices that represent a mesh.</param>
  39. /// <param name="normals">A list of normals that correspond to each vertex passed in via the vertices param.</param>
  40. /// <returns>A list of normals which are smoothed, or averaged, based on share vertex position.</returns>
  41. public static List<Vector3>
  42. CalculateSmoothNormals(IReadOnlyList<Vector3> vertices, IReadOnlyList<Vector3> normals) {
  43. var watch = System.Diagnostics.Stopwatch.StartNew();
  44. // Group all vertices that share the same location in space.
  45. var groupedVertices = new Dictionary<Vector3, List<KeyValuePair<int, Vector3>>>();
  46. for (int i = 0; i < vertices.Count; ++i) {
  47. var vertex = vertices[i];
  48. if (!groupedVertices.TryGetValue(vertex, out var group)) {
  49. group = new List<KeyValuePair<int, Vector3>>();
  50. groupedVertices[vertex] = group;
  51. }
  52. group.Add(new KeyValuePair<int, Vector3>(i, vertex));
  53. }
  54. var smoothNormals = new List<Vector3>(normals);
  55. // If we don't hit the degenerate case of each vertex is its own group (no vertices shared a location), average the normals of each group.
  56. if (groupedVertices.Count != vertices.Count) {
  57. foreach (var group in groupedVertices) {
  58. var smoothingGroup = group.Value;
  59. // No need to smooth a group of one.
  60. if (smoothingGroup.Count != 1) {
  61. var smoothedNormal = smoothingGroup.Aggregate(Vector3.zero,
  62. (current, vertex) => current + normals[vertex.Key]);
  63. smoothedNormal.Normalize();
  64. foreach (var vertex in smoothingGroup) {
  65. smoothNormals[vertex.Key] = smoothedNormal;
  66. }
  67. }
  68. }
  69. }
  70. Debug.Log($"<b>[Flat Kit]</b> Generated smooth normals for <i>{vertices.Count}</i> vertices in " +
  71. $"<i>{watch.ElapsedMilliseconds}</i> ms.");
  72. return smoothNormals;
  73. }
  74. }
  75. }