fork download
  1. /*
  2.  * Author: Zoodinger
  3.  *
  4.  */
  5.  
  6. using System;
  7. using System.Collections.Generic;
  8.  
  9. using UnityEngine;
  10.  
  11. namespace ZooUtils
  12. {
  13. public static class NormalSolver
  14. {
  15. /// <summary>
  16. /// Recalculate the normals of a given mesh but attempt to smooth distinct
  17. /// vertices that are in the same position.
  18. /// </summary>
  19. /// <param name="mesh"></param>
  20. /// <param name="angle">
  21. /// The smoothing angle. Note that triangles that already share
  22. /// the same vertex will be smooth regardless of the angle!
  23. /// </param>
  24. public static void Recalculate(Mesh mesh, float angle)
  25. {
  26. Recalculate(mesh, angle, 4);
  27. }
  28.  
  29. /// <summary>
  30. /// Recalculate the normals of a given mesh but attempt to smooth distinct
  31. /// vertices that are in the same position.
  32. /// </summary>
  33. public static void Recalculate(Mesh mesh)
  34. {
  35. Recalculate(mesh, 60, 4);
  36. }
  37.  
  38. /// <summary>
  39. /// Recalculate the normals of a mesh but attempt to smooth distinct vertices
  40. /// that are in the same position.
  41. /// </summary>
  42. /// <param name="mesh"></param>
  43. /// <param name="angle">
  44. /// The smoothing angle. Note that triangles that already share
  45. /// the same vertex will be smooth regardless of the angle!
  46. /// </param>
  47. /// <param name="digitTolerance">
  48. /// How many decimal digits to take into account. A
  49. /// good value is between 3 and 5, but it depends on your model. This value
  50. /// is clamped between 0 and 6.
  51. /// </param>
  52. public static void Recalculate(Mesh mesh, float angle, int digitTolerance)
  53. {
  54. digitTolerance = Mathf.Clamp(digitTolerance, 0, 6);
  55. VertexKey.DigitTolerance = 1;
  56.  
  57. for (int i = 0; i < digitTolerance; ++i) { VertexKey.DigitTolerance *= 10; }
  58.  
  59. var triangles = mesh.GetTriangles(0);
  60. var vertices = mesh.vertices;
  61. var triNormals = new Vector3[triangles.Length / 3]; //Holds the normal of each triangle
  62. var normals = new Vector3[vertices.Length];
  63.  
  64. var dictionary = new Dictionary<VertexKey, VertexEntry>();
  65.  
  66. for (int i = 0; i < triangles.Length; i += 3)
  67. {
  68. int i1 = triangles[i];
  69. int i2 = triangles[i + 1];
  70. int i3 = triangles[i + 2];
  71. //Calculate the normal of the triangle
  72. Vector3 p1 = vertices[i2] - vertices[i1];
  73. Vector3 p2 = vertices[i3] - vertices[i1];
  74. Vector3 normal = Vector3.Cross(p1, p2).normalized;
  75. int triIndex = i / 3;
  76. triNormals[triIndex] = normal;
  77.  
  78. VertexEntry entry;
  79. VertexKey key;
  80.  
  81. //For each of the three points of the triangle
  82. // > Add this triangle as part of the triangles they're connected to.
  83.  
  84. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry))
  85. {
  86. entry = new VertexEntry();
  87. dictionary.Add(key, entry);
  88. }
  89. entry.Add(i1, triIndex);
  90.  
  91. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry))
  92. {
  93. entry = new VertexEntry();
  94. dictionary.Add(key, entry);
  95. }
  96. entry.Add(i2, triIndex);
  97.  
  98. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry))
  99. {
  100. entry = new VertexEntry();
  101. dictionary.Add(key, entry);
  102. }
  103. entry.Add(i3, triIndex);
  104. }
  105.  
  106. //Foreach point in space (not necessarily the same vertex index!)
  107. //{
  108. // Foreach triangle T1 that point belongs to
  109. // {
  110. // Foreach other triangle T2 (including self) that point belongs to and that
  111. // meets any of the following:
  112. // 1) The corresponding vertex is actually the same vertex
  113. // 2) The angle between the two triangles is less than the smoothing angle
  114. // {
  115. // > Add to temporary Vector3
  116. // }
  117. // > Divide temporary Vector3 by count of T2 to find the average
  118. // > Assign the normal to corresponding vertex of T1
  119. // }
  120. //}
  121.  
  122. foreach (var value in dictionary.Values)
  123. {
  124. for (int i = 0; i < value.Count; ++i)
  125. {
  126. var sum = new Vector3();
  127. int countAvg = 0;
  128. for (int j = 0; j < value.Count; ++j)
  129. {
  130. if (value.VertexIndex[i] == value.VertexIndex[j]
  131. || AngleCheck(
  132. triNormals[value.TriangleIndex[i]],
  133. triNormals[value.TriangleIndex[j]],
  134. angle))
  135. {
  136. sum += triNormals[value.TriangleIndex[j]];
  137. ++countAvg;
  138. }
  139. }
  140.  
  141. sum /= countAvg;
  142. normals[value.VertexIndex[i]] = sum;
  143. }
  144. }
  145.  
  146. mesh.normals = normals;
  147. }
  148.  
  149. private static bool AngleCheck(Vector3 normal1, Vector3 normal2, float angle)
  150. {
  151. //We have to clamp this value, despite the fact that the dot result should always
  152. // return values between -1 and 1 inclusive. Sometimes, float inaccuracies make
  153. // Mathf.Acos return NaN!
  154. float dot = Mathf.Clamp(Vector3.Dot(normal1, normal2), -0.99999f, 0.99999f);
  155. float acos = Mathf.Acos(dot) * Mathf.Rad2Deg;
  156. return acos <= angle;
  157. }
  158.  
  159. private sealed class VertexEntry
  160. {
  161. public int[] TriangleIndex = new int[4];
  162. public int[] VertexIndex = new int[4];
  163.  
  164. private int _reserved = 4;
  165. public int Count { get; private set; }
  166.  
  167. public void Add(int vertIndex, int triIndex)
  168. {
  169. //Auto-resize the arrays when needed
  170. if (_reserved == Count)
  171. {
  172. _reserved *= 2;
  173. Array.Resize(ref TriangleIndex, _reserved);
  174. Array.Resize(ref VertexIndex, _reserved);
  175. }
  176. TriangleIndex[Count] = triIndex;
  177. VertexIndex[Count] = vertIndex;
  178. ++Count;
  179. }
  180. }
  181.  
  182. // If you want to have larger values, change int to long.
  183. // Note that 6-digit tolerance only allows values of about up to 1000 as coordinates.
  184. private sealed class VertexKey
  185. {
  186. private readonly int _x;
  187. private readonly int _y;
  188. private readonly int _z;
  189.  
  190. public static int DigitTolerance;
  191.  
  192. public VertexKey(Vector3 position)
  193. {
  194. _x = (int)(Math.Round(position.x * DigitTolerance));
  195. _y = (int)(Math.Round(position.y * DigitTolerance));
  196. _z = (int)(Math.Round(position.z * DigitTolerance));
  197. }
  198.  
  199. public override bool Equals(object obj)
  200. {
  201. var key = obj as VertexKey;
  202. return key != null && _x == key._x && _y == key._y && _z == key._z;
  203. }
  204.  
  205. public override int GetHashCode()
  206. {
  207. return (_x ^ _y ^ _z).GetHashCode();
  208. }
  209. }
  210. }
  211. }
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty