暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TerrainToMesh.cs 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using System.Collections.Generic;
  2. using Unity.Burst;
  3. using Unity.Collections;
  4. using Unity.Jobs;
  5. using Unity.Mathematics;
  6. namespace UnityEngine.Rendering.UnifiedRayTracing
  7. {
  8. internal class TerrainToMesh
  9. {
  10. static private AsyncTerrainToMeshRequest MakeAsyncTerrainToMeshRequest(int width, int height, Vector3 heightmapScale, float[,] heightmap, bool[,] holes)
  11. {
  12. int vertexCount = width * height;
  13. var job = new ComputeTerrainMeshJob();
  14. job.heightmap = new NativeArray<float>(vertexCount, Allocator.Persistent);
  15. for (int i = 0; i < vertexCount; ++i)
  16. job.heightmap[i] = heightmap[i / (width), i % (width)];
  17. job.holes = new NativeArray<bool>((width - 1) * (height - 1), Allocator.Persistent);
  18. for (int i = 0; i < (width - 1) * (height - 1); ++i)
  19. job.holes[i] = holes[i / (width - 1), i % (width - 1)];
  20. job.width = width;
  21. job.height = height;
  22. job.heightmapScale = heightmapScale;
  23. job.positions = new NativeArray<float3>(vertexCount, Allocator.Persistent);
  24. job.uvs = new NativeArray<float2>(vertexCount, Allocator.Persistent);
  25. job.normals = new NativeArray<float3>(vertexCount, Allocator.Persistent);
  26. job.indices = new NativeArray<int>((width - 1) * (height - 1) * 6, Allocator.Persistent);
  27. JobHandle jobHandle = job.Schedule(vertexCount, math.max(width, 128));
  28. return new AsyncTerrainToMeshRequest(job, jobHandle);
  29. }
  30. static public AsyncTerrainToMeshRequest ConvertAsync(Terrain terrain)
  31. {
  32. TerrainData terrainData = terrain.terrainData;
  33. int width = terrainData.heightmapTexture.width;
  34. int height = terrainData.heightmapTexture.height;
  35. float[,] heightmap = terrain.terrainData.GetHeights(0, 0, width, height);
  36. bool[,] holes = terrain.terrainData.GetHoles(0, 0, width - 1, height - 1);
  37. return MakeAsyncTerrainToMeshRequest(width, height, terrainData.heightmapScale, heightmap, holes);
  38. }
  39. static public Mesh Convert(Terrain terrain)
  40. {
  41. var request = ConvertAsync(terrain);
  42. request.WaitForCompletion();
  43. return request.GetMesh();
  44. }
  45. static public AsyncTerrainToMeshRequest ConvertAsync(int heightmapWidth, int heightmapHeight, short[] heightmapData, Vector3 heightmapScale, int holeWidth, int holeHeight, byte[] holedata)
  46. {
  47. float[,] heightmap = new float[heightmapWidth,heightmapHeight];
  48. for (int y = 0; y < heightmapHeight; ++y)
  49. for (int x = 0; x < heightmapWidth; ++x)
  50. heightmap[y, x] = (float)heightmapData[y * heightmapWidth + x] / (float)32766;
  51. bool[,] holes = new bool[heightmapWidth - 1, heightmapHeight - 1];
  52. if (holedata != null)
  53. {
  54. for (int y = 0; y < heightmapHeight - 1; ++y)
  55. for (int x = 0; x < heightmapWidth - 1; ++x)
  56. holes[y, x] = holedata[y * holeWidth + x] != 0;
  57. }
  58. else
  59. {
  60. for (int y = 0; y < heightmapHeight - 1; ++y)
  61. for (int x = 0; x < heightmapWidth - 1; ++x)
  62. holes[x, y] = true;
  63. }
  64. return MakeAsyncTerrainToMeshRequest(heightmapWidth, heightmapHeight, heightmapScale, heightmap, holes);
  65. }
  66. static public Mesh Convert(int heightmapWidth, int heightmapHeight, short[] heightmapData, Vector3 heightmapScale, int holeWidth, int holeHeight, byte[] holedata)
  67. {
  68. var request = ConvertAsync(heightmapWidth, heightmapHeight, heightmapData, heightmapScale, holeWidth, holeHeight, holedata);
  69. request.WaitForCompletion();
  70. return request.GetMesh();
  71. }
  72. }
  73. internal struct AsyncTerrainToMeshRequest
  74. {
  75. internal AsyncTerrainToMeshRequest(ComputeTerrainMeshJob job, JobHandle jobHandle)
  76. {
  77. m_Job = job;
  78. m_JobHandle = jobHandle;
  79. }
  80. public bool done { get { return m_JobHandle.IsCompleted; } }
  81. public Mesh GetMesh()
  82. {
  83. if (!done)
  84. return null;
  85. Mesh mesh = new Mesh();
  86. mesh.indexFormat = IndexFormat.UInt32;
  87. mesh.SetVertices(m_Job.positions);
  88. mesh.SetUVs(0, m_Job.uvs);
  89. mesh.SetNormals(m_Job.normals);
  90. mesh.SetIndices(TriangleIndicesWithoutHoles().ToArray(), MeshTopology.Triangles, 0);
  91. m_Job.DisposeArrays();
  92. return mesh;
  93. }
  94. public void WaitForCompletion()
  95. {
  96. m_JobHandle.Complete();
  97. }
  98. List<int> TriangleIndicesWithoutHoles()
  99. {
  100. var trianglesWithoutHoles = new List<int>((m_Job.width - 1) * (m_Job.height - 1) * 6);
  101. for (int i = 0; i < m_Job.indices.Length; i += 3)
  102. {
  103. int i1 = m_Job.indices[i];
  104. int i2 = m_Job.indices[i + 1];
  105. int i3 = m_Job.indices[i + 2];
  106. if (i1 != 0 && i2 != 0 && i3 != 0)
  107. {
  108. trianglesWithoutHoles.Add(i1);
  109. trianglesWithoutHoles.Add(i2);
  110. trianglesWithoutHoles.Add(i3);
  111. }
  112. }
  113. if (trianglesWithoutHoles.Count == 0)
  114. {
  115. trianglesWithoutHoles.Add(0);
  116. trianglesWithoutHoles.Add(0);
  117. trianglesWithoutHoles.Add(0);
  118. }
  119. return trianglesWithoutHoles;
  120. }
  121. JobHandle m_JobHandle;
  122. ComputeTerrainMeshJob m_Job;
  123. }
  124. [BurstCompile]
  125. internal struct ComputeTerrainMeshJob : IJobParallelFor
  126. {
  127. [ReadOnly]
  128. public NativeArray<float> heightmap;
  129. [ReadOnly]
  130. public NativeArray<bool> holes;
  131. public int width;
  132. public int height;
  133. public float3 heightmapScale;
  134. public NativeArray<float3> positions;
  135. public NativeArray<float2> uvs;
  136. public NativeArray<float3> normals;
  137. [NativeDisableParallelForRestriction]
  138. public NativeArray<int> indices;
  139. public void DisposeArrays()
  140. {
  141. heightmap.Dispose();
  142. holes.Dispose();
  143. positions.Dispose();
  144. uvs.Dispose();
  145. normals.Dispose();
  146. indices.Dispose();
  147. }
  148. public void Execute(int i)
  149. {
  150. int vertexIndex = i;
  151. int x = i % width;
  152. int y = i / height;
  153. float3 v = new float3(x, heightmap[y*width +x], y);
  154. positions[vertexIndex] = v * heightmapScale;
  155. uvs[vertexIndex] = v.xz / new float2(width, height);
  156. normals[vertexIndex] = CalculateTerrainNormal(heightmap, x, y, width, height, heightmapScale);
  157. if (x < width - 1 && y < height - 1)
  158. {
  159. int i1 = y * width + x;
  160. int i2 = i1 + 1;
  161. int i3 = i1 + width;
  162. int i4 = i3 + 1;
  163. int faceIndex = x + y * (width - 1);
  164. if (!holes[faceIndex])
  165. {
  166. i1 = i2 = i3 = i4 = 0;
  167. }
  168. indices[6* faceIndex + 0] = i1;
  169. indices[6* faceIndex + 1] = i4;
  170. indices[6* faceIndex + 2] = i2;
  171. indices[6* faceIndex + 3] = i1;
  172. indices[6* faceIndex + 4] = i3;
  173. indices[6* faceIndex + 5] = i4;
  174. }
  175. }
  176. static float3 CalculateTerrainNormal(NativeArray<float> heightmap, int x, int y, int width, int height, float3 scale)
  177. {
  178. float dY, dX;
  179. dX = SampleHeight(x - 1, y - 1, width, height, heightmap, scale.y) * -1.0F;
  180. dX += SampleHeight(x - 1, y, width, height, heightmap, scale.y) * -2.0F;
  181. dX += SampleHeight(x - 1, y + 1, width, height, heightmap, scale.y) * -1.0F;
  182. dX += SampleHeight(x + 1, y - 1, width, height, heightmap, scale.y) * 1.0F;
  183. dX += SampleHeight(x + 1, y, width, height, heightmap, scale.y) * 2.0F;
  184. dX += SampleHeight(x + 1, y + 1, width, height, heightmap, scale.y) * 1.0F;
  185. dX /= scale.x;
  186. dY = SampleHeight(x - 1, y - 1, width, height, heightmap, scale.y) * -1.0F;
  187. dY += SampleHeight(x, y - 1, width, height, heightmap, scale.y) * -2.0F;
  188. dY += SampleHeight(x + 1, y - 1, width, height, heightmap, scale.y) * -1.0F;
  189. dY += SampleHeight(x - 1, y + 1, width, height, heightmap, scale.y) * 1.0F;
  190. dY += SampleHeight(x, y + 1, width, height, heightmap, scale.y) * 2.0F;
  191. dY += SampleHeight(x + 1, y + 1, width, height, heightmap, scale.y) * 1.0F;
  192. dY /= scale.z;
  193. // Cross Product of components of gradient reduces to
  194. return math.normalize(new float3(-dX, 8, -dY));
  195. }
  196. static float SampleHeight(int x, int y, int width, int height, NativeArray<float> heightmap, float scale)
  197. {
  198. x = math.clamp(x, 0, width - 1);
  199. y = math.clamp(y, 0, height - 1);
  200. return heightmap[x + y* width] * scale;
  201. }
  202. }
  203. }