MeshHelper.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //
  2. // Procedural Lightning for Unity
  3. // (c) 2015 Digital Ruby, LLC
  4. // Source code may be used for personal or commercial projects.
  5. // Source code may NOT be redistributed or sold.
  6. //
  7. using System.Collections.Generic;
  8. using UnityEngine;
  9. namespace DigitalRuby.ThunderAndLightning
  10. {
  11. /// <summary>
  12. /// Mesh helper methods
  13. /// </summary>
  14. public class MeshHelper
  15. {
  16. private Mesh mesh;
  17. private int[] triangles;
  18. private Vector3[] vertices;
  19. private Vector3[] normals;
  20. private float[] normalizedAreaWeights;
  21. /// <summary>
  22. /// Constructor
  23. /// </summary>
  24. /// <param name="mesh">Mesh</param>
  25. public MeshHelper(Mesh mesh)
  26. {
  27. this.mesh = mesh;
  28. this.triangles = mesh.triangles;
  29. this.vertices = mesh.vertices;
  30. this.normals = mesh.normals;
  31. CalculateNormalizedAreaWeights();
  32. }
  33. /// <summary>
  34. /// Get random point on mesh
  35. /// </summary>
  36. /// <param name="hit">Raycast</param>
  37. /// <param name="triangleIndex">Received indice</param>
  38. public void GenerateRandomPoint(ref RaycastHit hit, out int triangleIndex)
  39. {
  40. triangleIndex = SelectRandomTriangle();
  41. GetRaycastFromTriangleIndex(triangleIndex, ref hit);
  42. }
  43. /// <summary>
  44. /// Get ray dir from indice
  45. /// </summary>
  46. /// <param name="triangleIndex">Indice</param>
  47. /// <param name="hit">Raycast result</param>
  48. public void GetRaycastFromTriangleIndex(int triangleIndex, ref RaycastHit hit)
  49. {
  50. Vector3 bc = GenerateRandomBarycentricCoordinates();
  51. Vector3 p1 = vertices[triangles[triangleIndex]];
  52. Vector3 p2 = vertices[triangles[triangleIndex + 1]];
  53. Vector3 p3 = vertices[triangles[triangleIndex + 2]];
  54. hit.barycentricCoordinate = bc;
  55. hit.point = ((p1 * bc.x) + (p2 * bc.y) + (p3 * bc.z));
  56. if (normals == null)
  57. {
  58. // face normal
  59. hit.normal = Vector3.Cross((p3 - p2), (p1 - p2)).normalized;
  60. }
  61. else
  62. {
  63. // interpolated vertex normal
  64. p1 = normals[triangles[triangleIndex]];
  65. p2 = normals[triangles[triangleIndex + 1]];
  66. p3 = normals[triangles[triangleIndex + 2]];
  67. hit.normal = (p1 * bc.x) + (p2 * bc.y) + (p3 * bc.z);
  68. }
  69. }
  70. /// <summary>
  71. /// Mesh
  72. /// </summary>
  73. public Mesh Mesh
  74. {
  75. get { return mesh; }
  76. }
  77. /// <summary>
  78. /// Indices
  79. /// </summary>
  80. public int[] Triangles
  81. {
  82. get { return triangles; }
  83. }
  84. /// <summary>
  85. /// Vertices
  86. /// </summary>
  87. public Vector3[] Vertices
  88. {
  89. get { return vertices; }
  90. }
  91. /// <summary>
  92. /// Normals
  93. /// </summary>
  94. public Vector3[] Normals
  95. {
  96. get { return normals; }
  97. }
  98. private float[] CalculateSurfaceAreas(out float totalSurfaceArea)
  99. {
  100. int idx = 0;
  101. totalSurfaceArea = 0.0f;
  102. float[] surfaceAreas = new float[triangles.Length / 3];
  103. for (int triangleIndex = 0; triangleIndex < triangles.Length; triangleIndex += 3)
  104. {
  105. Vector3 p1 = vertices[triangles[triangleIndex]];
  106. Vector3 p2 = vertices[triangles[triangleIndex + 1]];
  107. Vector3 p3 = vertices[triangles[triangleIndex + 2]];
  108. // http://www.wikihow.com/Sample/Area-of-a-Triangle-Side-Length
  109. float a = (p1 - p2).sqrMagnitude;
  110. float b = (p1 - p3).sqrMagnitude;
  111. float c = (p2 - p3).sqrMagnitude;
  112. // faster with only 1 square root: http://www.iquilezles.org/blog/?p=1579
  113. // A² = (2ab + 2bc + 2ca – a² – b² – c²)/16
  114. float areaSquared = ((2.0f * a * b) + (2.0f * b * c) + (2.0f * c * a) - (a * a) - (b * b) - (c * c)) / 16.0f;
  115. float area = PathGenerator.SquareRoot(areaSquared);
  116. surfaceAreas[idx++] = area;
  117. totalSurfaceArea += area;
  118. }
  119. return surfaceAreas;
  120. }
  121. private void CalculateNormalizedAreaWeights()
  122. {
  123. // create a sorted array of normalized area weights - this is an aggregate and is easily binary searched with a random value between 0 and 1 to find
  124. // a random triangle. Larger triangles have bigger gaps in the array.
  125. float totalSurfaceArea;
  126. normalizedAreaWeights = CalculateSurfaceAreas(out totalSurfaceArea);
  127. if (normalizedAreaWeights.Length == 0)
  128. {
  129. return;
  130. }
  131. float normalizedArea;
  132. float normalizedAggregate = 0.0f;
  133. for (int i = 0; i < normalizedAreaWeights.Length; i++)
  134. {
  135. normalizedArea = normalizedAreaWeights[i] / totalSurfaceArea;
  136. normalizedAreaWeights[i] = normalizedAggregate;
  137. normalizedAggregate += normalizedArea;
  138. }
  139. }
  140. private int SelectRandomTriangle()
  141. {
  142. float randomValue = Random.value;
  143. int imin = 0;
  144. int imax = normalizedAreaWeights.Length - 1;
  145. while (imin < imax)
  146. {
  147. int imid = (imin + imax) / 2;
  148. if (normalizedAreaWeights[imid] < randomValue)
  149. {
  150. imin = imid + 1;
  151. }
  152. else
  153. {
  154. imax = imid;
  155. }
  156. }
  157. return imin * 3;
  158. }
  159. private Vector3 GenerateRandomBarycentricCoordinates()
  160. {
  161. Vector3 barycentric = new Vector3(Random.Range(Mathf.Epsilon, 1.0f), Random.Range(Mathf.Epsilon, 1.0f), Random.Range(Mathf.Epsilon, 1.0f));
  162. // normalize the barycentric coordinates. These are normalized such that x + y + z = 1, as opposed to
  163. // normal vectors which are normalized such that Sqrt(x^2 + y^2 + z^2) = 1. See:
  164. // http://en.wikipedia.org/wiki/Barycentric_coordinate_system
  165. return barycentric / (barycentric.x + barycentric.y + barycentric.z);
  166. }
  167. }
  168. }