| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- public class EndlessTerrain : MonoBehaviour
- {
- const float viewerMoveThresholdForChunkUpdate = 25f;
- const float sqrViewerMoveThresholdForChunkUpdate = viewerMoveThresholdForChunkUpdate * viewerMoveThresholdForChunkUpdate;
- public LODInfo[] detailLevels;
- public static float maxViewDist;
- public Transform viewer;
- public Material mapMaterial;
- public static Vector2 viewerPosition;
- Vector2 viewerPositionOld;
- static MapGenerator mapGenerator;
- int chunkSize;
- int chunksVisibleInViewDst;
- Dictionary<Vector2, TerrainChunk> terrainChunkDictionary = new Dictionary<Vector2, TerrainChunk>();
- static List<TerrainChunk> terrainChunksVisibleLastUpdate = new List<TerrainChunk>();
- private void Start()
- {
- mapGenerator = FindFirstObjectByType<MapGenerator>();
- maxViewDist = detailLevels[detailLevels.Length - 1].visibleDstThreshold;
- chunkSize = mapGenerator.mapChunkSize - 1;
- chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDist / chunkSize);
- UpdateVisibleChunks();
- }
- private void Update()
- {
- viewerPosition = new Vector2(viewer.position.x, viewer.position.z) / mapGenerator.terrainData.uniformScale;
- if((viewerPositionOld-viewerPosition).sqrMagnitude > sqrViewerMoveThresholdForChunkUpdate)
- {
- viewerPositionOld = viewerPosition;
- UpdateVisibleChunks();
- }
- }
- private void OnDisable()
- {
- terrainChunksVisibleLastUpdate.Clear();
- }
- void UpdateVisibleChunks()
- {
- for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++)
- {
- terrainChunksVisibleLastUpdate[i].SetVisible(false);
- }
- terrainChunksVisibleLastUpdate.Clear();
- int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
- int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y / chunkSize);
- for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++)
- {
- for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
- {
- Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
- if (terrainChunkDictionary.ContainsKey(viewedChunkCoord))
- {
- terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
- }
- else
- {
- terrainChunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, chunkSize, detailLevels, transform, mapMaterial));
- }
- }
- }
- }
- public class TerrainChunk
- {
- GameObject meshObject;
- Vector2 position;
- Bounds bounds;
- MeshRenderer meshRenderer;
- MeshFilter meshFilter;
- MeshCollider meshCollider;
- LODInfo[] detailLevels;
- LODMesh[] lodMeshes;
- LODMesh collisionLODMesh;
- MapData mapData;
- bool mapDataReceived;
- int previousLODIndex = -1;
- public TerrainChunk(Vector2 coord, int size, LODInfo[] detailLevels, Transform parent, Material material)
- {
- this.detailLevels = detailLevels;
- position = coord * size;
- bounds = new Bounds(position, Vector2.one * size);
- Vector3 positionV3 = new Vector3(position.x, 0, position.y);
- meshObject = new GameObject("Terrain Chunk");
- meshObject.tag = "Floor";
- meshRenderer = meshObject.AddComponent<MeshRenderer>();
- meshFilter = meshObject.AddComponent<MeshFilter>();
- meshRenderer.material = material;
- meshObject.transform.position = positionV3 * mapGenerator.terrainData.uniformScale;
- meshObject.transform.parent = parent;
- meshObject.transform.localScale = Vector3.one * mapGenerator.terrainData.uniformScale;
- meshCollider = meshObject.AddComponent<MeshCollider>();
- SetVisible(false);
- lodMeshes = new LODMesh[detailLevels.Length];
- for (int i = 0; i < detailLevels.Length; i++)
- {
- lodMeshes[i] = new LODMesh(detailLevels[i].lod, UpdateTerrainChunk);
- if (detailLevels[i].useForCollider)
- {
- collisionLODMesh = lodMeshes[i];
- }
- }
- mapGenerator.RequestMapData(position,OnMapDataReceived);
- }
- void OnMapDataReceived(MapData mapData)
- {
- this.mapData = mapData;
- mapDataReceived = true;
- UpdateTerrainChunk();
- }
- public void UpdateTerrainChunk()
- {
- if (mapDataReceived)
- {
- float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
- bool visible = viewerDstFromNearestEdge <= maxViewDist;
- if (visible)
- {
- int lodIndex = 0;
- for (int i = 0; i < detailLevels.Length - 1; i++)
- {
- if (viewerDstFromNearestEdge > detailLevels[i].visibleDstThreshold)
- {
- lodIndex = i + 1;
- }
- else
- {
- break;
- }
- }
- if (lodIndex != previousLODIndex)
- {
- LODMesh lodMesh = lodMeshes[lodIndex];
- if (lodMesh.hasMesh)
- {
- previousLODIndex = lodIndex;
- meshFilter.mesh = lodMesh.mesh;
- }
- else if (!lodMesh.hasRequestedMesh)
- {
- lodMesh.RequestMesh(mapData);
- }
- }
- if(lodIndex == 0)
- {
- if (collisionLODMesh.hasMesh)
- {
- meshCollider.sharedMesh = collisionLODMesh.mesh;
- }
- else if (!collisionLODMesh.hasRequestedMesh)
- {
- collisionLODMesh.RequestMesh(mapData);
- }
- }
- terrainChunksVisibleLastUpdate.Add(this);
- }
- SetVisible(visible);
- }
-
- }
- public void SetVisible(bool visible)
- {
- meshObject.SetActive(visible);
- }
- public bool IsVisible()
- {
- return meshObject.activeSelf;
- }
- }
- class LODMesh
- {
- public Mesh mesh;
- public bool hasRequestedMesh;
- public bool hasMesh;
- int lod;
- System.Action updateCallback;
- public LODMesh(int lod, System.Action updateCallback)
- {
- this.lod = lod;
- this.updateCallback = updateCallback;
- }
-
- void OnMeshDataReceived(MeshData meshData)
- {
- mesh = meshData.CreateMesh();
- hasMesh = true;
- updateCallback();
- }
- public void RequestMesh(MapData mapData)
- {
- hasRequestedMesh = true;
- mapGenerator.RequestMeshData(mapData, lod, OnMeshDataReceived);
- }
- }
- [System.Serializable]
- public struct LODInfo
- {
- public int lod;
- public float visibleDstThreshold;
- public bool useForCollider;
- }
- }
|