EndlessTerrain.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. public class EndlessTerrain : MonoBehaviour
  6. {
  7. const float scale = 5f;
  8. const float viewerMoveThresholdForChunkUpdate = 25f;
  9. const float sqrViewerMoveThresholdForChunkUpdate = viewerMoveThresholdForChunkUpdate * viewerMoveThresholdForChunkUpdate;
  10. public LODInfo[] detailLevels;
  11. public static float maxViewDist;
  12. public Transform viewer;
  13. public Material mapMaterial;
  14. public static Vector2 viewerPosition;
  15. Vector2 viewerPositionOld;
  16. static MapGenerator mapGenerator;
  17. int chunkSize;
  18. int chunksVisibleInViewDst;
  19. Dictionary<Vector2, TerrainChunk> terrainChunkDictionary = new Dictionary<Vector2, TerrainChunk>();
  20. static List<TerrainChunk> terrainChunksVisibleLastUpdate = new List<TerrainChunk>();
  21. private void Start()
  22. {
  23. mapGenerator = FindFirstObjectByType<MapGenerator>();
  24. maxViewDist = detailLevels[detailLevels.Length - 1].visibleDstThreshold;
  25. chunkSize = MapGenerator.mapChunkSize - 1;
  26. chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDist / chunkSize);
  27. UpdateVisibleChunks();
  28. }
  29. private void Update()
  30. {
  31. viewerPosition = new Vector2(viewer.position.x, viewer.position.z) / scale;
  32. if((viewerPositionOld-viewerPosition).sqrMagnitude > sqrViewerMoveThresholdForChunkUpdate)
  33. {
  34. viewerPositionOld = viewerPosition;
  35. UpdateVisibleChunks();
  36. }
  37. }
  38. void UpdateVisibleChunks()
  39. {
  40. for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++)
  41. {
  42. terrainChunksVisibleLastUpdate[i].SetVisible(false);
  43. }
  44. terrainChunksVisibleLastUpdate.Clear();
  45. int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
  46. int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y / chunkSize);
  47. for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++)
  48. {
  49. for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
  50. {
  51. Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
  52. if (terrainChunkDictionary.ContainsKey(viewedChunkCoord))
  53. {
  54. terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
  55. }
  56. else
  57. {
  58. terrainChunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, chunkSize, detailLevels, transform, mapMaterial));
  59. }
  60. }
  61. }
  62. }
  63. public class TerrainChunk
  64. {
  65. GameObject meshObject;
  66. Vector2 position;
  67. Bounds bounds;
  68. MeshRenderer meshRenderer;
  69. MeshFilter meshFilter;
  70. LODInfo[] detailLevels;
  71. LODMesh[] lodMeshes;
  72. MapData mapData;
  73. bool mapDataReceived;
  74. int previousLODIndex = -1;
  75. public TerrainChunk(Vector2 coord, int size, LODInfo[] detailLevels, Transform parent, Material material)
  76. {
  77. this.detailLevels = detailLevels;
  78. position = coord * size;
  79. bounds = new Bounds(position, Vector2.one * size);
  80. Vector3 positionV3 = new Vector3(position.x, 0, position.y);
  81. meshObject = new GameObject("Terrain Chunk");
  82. meshRenderer = meshObject.AddComponent<MeshRenderer>();
  83. meshFilter = meshObject.AddComponent<MeshFilter>();
  84. meshRenderer.material = material;
  85. meshObject.transform.position = positionV3 * scale;
  86. meshObject.transform.parent = parent;
  87. meshObject.transform.localScale = Vector3.one * scale;
  88. SetVisible(false);
  89. lodMeshes = new LODMesh[detailLevels.Length];
  90. for (int i = 0; i < detailLevels.Length; i++)
  91. {
  92. lodMeshes[i] = new LODMesh(detailLevels[i].lod, UpdateTerrainChunk);
  93. }
  94. mapGenerator.RequestMapData(position,OnMapDataReceived);
  95. }
  96. void OnMapDataReceived(MapData mapData)
  97. {
  98. this.mapData = mapData;
  99. mapDataReceived = true;
  100. Texture2D texture = TextureGenerator.TextureFromColorMap(mapData.colorMap, MapGenerator.mapChunkSize, MapGenerator.mapChunkSize);
  101. meshRenderer.material.mainTexture = texture;
  102. UpdateTerrainChunk();
  103. }
  104. public void UpdateTerrainChunk()
  105. {
  106. if (mapDataReceived)
  107. {
  108. float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
  109. bool visible = viewerDstFromNearestEdge <= maxViewDist;
  110. if (visible)
  111. {
  112. int lodIndex = 0;
  113. for (int i = 0; i < detailLevels.Length - 1; i++)
  114. {
  115. if (viewerDstFromNearestEdge > detailLevels[i].visibleDstThreshold)
  116. {
  117. lodIndex = i + 1;
  118. }
  119. else
  120. {
  121. break;
  122. }
  123. }
  124. if (lodIndex != previousLODIndex)
  125. {
  126. LODMesh lodMesh = lodMeshes[lodIndex];
  127. if (lodMesh.hasMesh)
  128. {
  129. previousLODIndex = lodIndex;
  130. meshFilter.mesh = lodMesh.mesh;
  131. }
  132. else if (!lodMesh.hasRequestedMesh)
  133. {
  134. lodMesh.RequestMesh(mapData);
  135. }
  136. }
  137. terrainChunksVisibleLastUpdate.Add(this);
  138. }
  139. SetVisible(visible);
  140. }
  141. }
  142. public void SetVisible(bool visible)
  143. {
  144. meshObject.SetActive(visible);
  145. }
  146. public bool IsVisible()
  147. {
  148. return meshObject.activeSelf;
  149. }
  150. }
  151. class LODMesh
  152. {
  153. public Mesh mesh;
  154. public bool hasRequestedMesh;
  155. public bool hasMesh;
  156. int lod;
  157. System.Action updateCallback;
  158. public LODMesh(int lod, System.Action updateCallback)
  159. {
  160. this.lod = lod;
  161. this.updateCallback = updateCallback;
  162. }
  163. void OnMeshDataReceived(MeshData meshData)
  164. {
  165. mesh = meshData.CreateMesh();
  166. hasMesh = true;
  167. updateCallback();
  168. }
  169. public void RequestMesh(MapData mapData)
  170. {
  171. hasRequestedMesh = true;
  172. mapGenerator.RequestMeshData(mapData, lod, OnMeshDataReceived);
  173. }
  174. }
  175. [System.Serializable]
  176. public struct LODInfo
  177. {
  178. public int lod;
  179. public float visibleDstThreshold;
  180. }
  181. }