MeshGenerator.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using UnityEngine;
  2. namespace VLB
  3. {
  4. public static class MeshGenerator
  5. {
  6. const float kMinTruncatedRadius = 0.001f;
  7. static float GetAngleOffset(int numSides)
  8. {
  9. // rotate square beams so they are properly oriented to scale them more easily
  10. return numSides == 4 ? (Mathf.PI * 0.25f) : 0f;
  11. }
  12. public static Mesh GenerateConeZ_RadiusAndAngle(float lengthZ, float radiusStart, float coneAngle, int numSides, int numSegments, bool cap, bool doubleSided)
  13. {
  14. Debug.Assert(lengthZ > 0f);
  15. Debug.Assert(coneAngle > 0f && coneAngle < 180f);
  16. var radiusEnd = lengthZ * Mathf.Tan(coneAngle * Mathf.Deg2Rad * 0.5f);
  17. return GenerateConeZ_Radius(lengthZ, radiusStart, radiusEnd, numSides, numSegments, cap, doubleSided);
  18. }
  19. public static Mesh GenerateConeZ_Angle(float lengthZ, float coneAngle, int numSides, int numSegments, bool cap, bool doubleSided)
  20. {
  21. return GenerateConeZ_RadiusAndAngle(lengthZ, 0f, coneAngle, numSides, numSegments, cap, doubleSided);
  22. }
  23. public static Mesh GenerateConeZ_Radius(float lengthZ, float radiusStart, float radiusEnd, int numSides, int numSegments, bool cap, bool doubleSided)
  24. {
  25. Debug.Assert(lengthZ > 0f);
  26. Debug.Assert(radiusStart >= 0f);
  27. Debug.Assert(numSides >= 3);
  28. Debug.Assert(numSegments >= 0);
  29. var mesh = new Mesh();
  30. bool genCap = cap && radiusStart > 0f;
  31. // We use the XY position of the vertices to compute the cone normal in the shader.
  32. // With a perfectly sharp cone, we couldn't compute accurate normals at its top.
  33. radiusStart = Mathf.Max(radiusStart, kMinTruncatedRadius);
  34. int vertCountSides = numSides * (numSegments + 2);
  35. int vertCountTotal = vertCountSides;
  36. if (genCap)
  37. vertCountTotal += numSides + 1;
  38. // VERTICES
  39. {
  40. float angleOffset = GetAngleOffset(numSides);
  41. var vertices = new Vector3[vertCountTotal];
  42. for (int i = 0; i < numSides; i++)
  43. {
  44. float angle = angleOffset + 2 * Mathf.PI * i / numSides;
  45. float angleCos = Mathf.Cos(angle);
  46. float angleSin = Mathf.Sin(angle);
  47. for (int seg = 0; seg < numSegments + 2; seg++)
  48. {
  49. float tseg = (float)seg / (numSegments + 1);
  50. Debug.Assert(tseg >= 0f && tseg <= 1f);
  51. float radius = Mathf.Lerp(radiusStart, radiusEnd, tseg);
  52. vertices[i + seg * numSides] = new Vector3(radius * angleCos, radius * angleSin, tseg * lengthZ);
  53. }
  54. }
  55. if (genCap)
  56. {
  57. int ind = vertCountSides;
  58. vertices[ind] = Vector3.zero;
  59. ind++;
  60. for (int i = 0; i < numSides; i++)
  61. {
  62. float angle = angleOffset + 2 * Mathf.PI * i / numSides;
  63. float angleCos = Mathf.Cos(angle);
  64. float angleSin = Mathf.Sin(angle);
  65. vertices[ind] = new Vector3(radiusStart * angleCos, radiusStart * angleSin, 0f);
  66. ind++;
  67. }
  68. Debug.Assert(ind == vertices.Length);
  69. }
  70. if (!doubleSided)
  71. {
  72. mesh.vertices = vertices;
  73. }
  74. else
  75. {
  76. var vertices2 = new Vector3[vertices.Length * 2];
  77. vertices.CopyTo(vertices2, 0);
  78. vertices.CopyTo(vertices2, vertices.Length);
  79. mesh.vertices = vertices2;
  80. }
  81. }
  82. // UV (used to flags vertices as sides or cap)
  83. // X: 0 = sides ; 1 = cap
  84. // Y: 0 = front face ; 1 = back face (doubleSided only)
  85. {
  86. var uv = new Vector2[vertCountTotal];
  87. int ind = 0;
  88. for (int i = 0; i < vertCountSides; i++)
  89. uv[ind++] = Vector2.zero;
  90. if (genCap)
  91. {
  92. for (int i = 0; i < numSides + 1; i++)
  93. uv[ind++] = new Vector2(1, 0);
  94. }
  95. Debug.Assert(ind == uv.Length);
  96. if (!doubleSided)
  97. {
  98. mesh.uv = uv;
  99. }
  100. else
  101. {
  102. var uv2 = new Vector2[uv.Length * 2];
  103. uv.CopyTo(uv2, 0);
  104. uv.CopyTo(uv2, uv.Length);
  105. for (int i = 0; i < uv.Length; i++)
  106. {
  107. var value = uv2[i + uv.Length];
  108. uv2[i + uv.Length] = new Vector2(value.x, 1);
  109. }
  110. mesh.uv = uv2;
  111. }
  112. }
  113. // INDICES
  114. {
  115. int triCountSides = numSides * 2 * Mathf.Max(numSegments + 1, 1);
  116. int indCountSides = triCountSides * 3;
  117. int indCountTotal = indCountSides;
  118. if (genCap)
  119. indCountTotal += numSides * 3;
  120. var indices = new int[indCountTotal];
  121. int ind = 0;
  122. for (int i = 0; i < numSides; i++)
  123. {
  124. int ip1 = i + 1;
  125. if (ip1 == numSides)
  126. ip1 = 0;
  127. for (int k = 0; k < numSegments + 1; ++k)
  128. {
  129. var offset = k * numSides;
  130. indices[ind++] = offset + i;
  131. indices[ind++] = offset + ip1;
  132. indices[ind++] = offset + i + numSides;
  133. indices[ind++] = offset + ip1 + numSides;
  134. indices[ind++] = offset + i + numSides;
  135. indices[ind++] = offset + ip1;
  136. }
  137. }
  138. if (genCap)
  139. {
  140. for (int i = 0; i < numSides - 1; i++)
  141. {
  142. indices[ind++] = vertCountSides;
  143. indices[ind++] = vertCountSides + i + 2;
  144. indices[ind++] = vertCountSides + i + 1;
  145. }
  146. indices[ind++] = vertCountSides;
  147. indices[ind++] = vertCountSides + 1;
  148. indices[ind++] = vertCountSides + numSides;
  149. }
  150. Debug.Assert(ind == indices.Length);
  151. if (!doubleSided)
  152. {
  153. mesh.triangles = indices;
  154. }
  155. else
  156. {
  157. var indices2 = new int[indices.Length * 2];
  158. indices.CopyTo(indices2, 0);
  159. for (int i = 0; i < indices.Length; i += 3)
  160. {
  161. indices2[indices.Length + i + 0] = indices[i + 0] + vertCountTotal;
  162. indices2[indices.Length + i + 1] = indices[i + 2] + vertCountTotal;
  163. indices2[indices.Length + i + 2] = indices[i + 1] + vertCountTotal;
  164. }
  165. mesh.triangles = indices2;
  166. }
  167. }
  168. mesh.bounds = ComputeBounds(lengthZ, radiusStart, radiusEnd);
  169. Debug.Assert(mesh.vertexCount == GetVertexCount(numSides, numSegments, genCap, doubleSided));
  170. Debug.Assert(mesh.triangles.Length == GetIndicesCount(numSides, numSegments, genCap, doubleSided));
  171. return mesh;
  172. }
  173. public static Bounds ComputeBounds(float lengthZ, float radiusStart, float radiusEnd)
  174. {
  175. float maxDiameter = Mathf.Max(radiusStart, radiusEnd) * 2;
  176. return new Bounds(
  177. new Vector3(0, 0, lengthZ * 0.5f),
  178. new Vector3(maxDiameter, maxDiameter, lengthZ)
  179. );
  180. }
  181. public static int GetVertexCount(int numSides, int numSegments, bool geomCap, bool doubleSided)
  182. {
  183. Debug.Assert(numSides >= 2);
  184. Debug.Assert(numSegments >= 0);
  185. int count = numSides * (numSegments + 2);
  186. if (geomCap) count += numSides + 1;
  187. if (doubleSided) count *= 2;
  188. return count;
  189. }
  190. public static int GetIndicesCount(int numSides, int numSegments, bool geomCap, bool doubleSided)
  191. {
  192. Debug.Assert(numSides >= 2);
  193. Debug.Assert(numSegments >= 0);
  194. int count = numSides * (numSegments + 1) * 2 * 3;
  195. if (geomCap) count += numSides * 3;
  196. if (doubleSided) count *= 2;
  197. return count;
  198. }
  199. public static int GetSharedMeshVertexCount()
  200. {
  201. return GetVertexCount(Config.Instance.sharedMeshSides, Config.Instance.sharedMeshSegments, true, Config.Instance.requiresDoubleSidedMesh);
  202. }
  203. public static int GetSharedMeshIndicesCount()
  204. {
  205. return GetIndicesCount(Config.Instance.sharedMeshSides, Config.Instance.sharedMeshSegments, true, Config.Instance.requiresDoubleSidedMesh);
  206. }
  207. }
  208. }