using System.Collections.Generic; using UnityEditor; using UnityEngine; public class ChunkGameObject : MonoBehaviour { static float basisChunkGroese = 5f; Renderer meshRenderer; MeshFilter meshFilter; List verts = new List(); List indes = new List(); List tipOffsets_missingInNextLOD = new List(); GameObject GrasChunkPrefab; public GrasFeld grasFeld; public Vector2Int pos; public Texture2D DebugGrasTextur; public bool hasChanged = false; public int myLOD = 0; public ChunkGameObject[] childChunks = new ChunkGameObject[4]; [SerializeField] public List materials = new List(); public Mesh LOD_a; public Mesh LOD_b; public bool doUpdate = false; private void OnEnable() { meshRenderer = GetComponent(); meshFilter = GetComponent(); return; if (myLOD != 0 && transform.childCount == 4) { childChunks[0] = transform.GetChild(0).GetComponent(); childChunks[1] = transform.GetChild(1).GetComponent(); childChunks[2] = transform.GetChild(2).GetComponent(); childChunks[3] = transform.GetChild(3).GetComponent(); } else childChunks = null; } public void SetUpdate(Vector2Int chunkPos) { doUpdate = false; if (ContainsChunk(chunkPos) == false) return; doUpdate = true; if(myLOD > 0) for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) { int childLodSize = (int)basisChunkGroese * (int)Mathf.Pow(2, myLOD - 1); Vector2Int currentChildPos = new Vector2Int((int)transform.position.x + i * childLodSize, (int)transform.position.z + j * childLodSize); if (ContainsChunk(chunkPos, currentChildPos, myLOD - 1) == false) continue; var currenChild = childChunks[i * 2 + j]; if (currenChild == null) { if (GrasChunkPrefab == null) GrasChunkPrefab = Resources.Load("GrasChunk") as GameObject; GameObject tempGO = Instantiate(GrasChunkPrefab); tempGO.transform.position = new Vector3(currentChildPos.x, 0, currentChildPos.y); tempGO.transform.parent = transform; tempGO.name = "Chunk LOD" + (myLOD - 1); var chunkObject = tempGO.GetComponent(); chunkObject.myLOD = myLOD - 1; chunkObject.grasFeld = this.grasFeld; //chunkObject.SetPosToGrid(); childChunks[i * 2 + j] = chunkObject; currenChild = chunkObject; } currenChild.SetUpdate(chunkPos); } } public void SetLOD(Vector3 cameraPosition) { if (meshRenderer == null) meshRenderer = GetComponent(); float LODSize = basisChunkGroese * Mathf.Pow(2f, myLOD); Vector3 nearestPointToCamera = new Vector3( Mathf.Min(transform.position.x + LODSize, Mathf.Max(transform.position.x, cameraPosition.x)), Mathf.Min(transform.position.y + LODSize, Mathf.Max(transform.position.y, cameraPosition.y)), Mathf.Min(transform.position.z + LODSize, Mathf.Max(transform.position.z, cameraPosition.z)) ); float distanceToNearestCameraPosition = Vector3.Distance(nearestPointToCamera, cameraPosition); if(distanceToNearestCameraPosition < LODSize / 2f) { if (distanceToNearestCameraPosition < LODSize) GetComponent().sharedMesh = LOD_a; else GetComponent().sharedMesh = LOD_b; if (myLOD == 0) { meshRenderer.enabled = true; return; } meshRenderer.enabled = false; for (int i = 0; i < 4; i++) { if (childChunks[i] == null) continue; childChunks[i].gameObject.SetActive(true); childChunks[i].SetLOD(cameraPosition); } } else { if(distanceToNearestCameraPosition < LODSize) GetComponent().sharedMesh = LOD_a; else GetComponent().sharedMesh = LOD_b; meshRenderer.enabled = true; for (int i = 0; i < 4; i++) { if (childChunks[i] == null) continue; childChunks[i].gameObject.SetActive(false); } } } public void DoMeshUpdate() { if (grasFeld == null) return; if (doUpdate == false) return; doUpdate = false; verts.Clear(); indes.Clear(); tipOffsets_missingInNextLOD.Clear(); if (myLOD > 0) { for(int i = 0; i < 4; i++) { if (childChunks[i] != null) childChunks[i].DoMeshUpdate(); } //Das Gras der Kindobjekte in den aktuellen Chunk hinzufuegen for (int i = 0; i < 4; i++) { if (childChunks[i] != null) { foreach (Vector3 v in childChunks[i].LOD_b.vertices) { verts.Add(v + childChunks[i].transform.position - transform.position); } List tempUvs = new List(); childChunks[i].LOD_b.GetUVs(0, tempUvs); tipOffsets_missingInNextLOD.AddRange(tempUvs); } } for (int i = 0; i < verts.Count - 1; i++) { indes.Add(i); } if (LOD_a == null) LOD_a = new Mesh(); LOD_a.Clear(); LOD_a.vertices = verts.ToArray(); LOD_a.SetUVs(0, tipOffsets_missingInNextLOD); LOD_a.SetIndices(indes.ToArray(), MeshTopology.Points, 0); LOD_a.RecalculateBounds(); GetComponent().sharedMesh = LOD_a; GetComponent().sharedMaterial = materials[myLOD]; //Die helfte des grases entfernen int initCount = verts.Count / 2; for (int i = 0; i < initCount; i++) { int randomIndex = Random.Range(0, verts.Count - 1); verts.RemoveAt(randomIndex); tipOffsets_missingInNextLOD.RemoveAt(randomIndex); } indes.Clear(); for (int i=0; i < verts.Count - 1; i++) { indes.Add(i); } if (LOD_b == null) LOD_b = new Mesh(); LOD_b.Clear(); LOD_b.vertices = verts.ToArray(); LOD_b.SetUVs(0, tipOffsets_missingInNextLOD); LOD_b.SetIndices(indes.ToArray(), MeshTopology.Points, 0); LOD_b.RecalculateBounds(); } else CreateMeshForLOD_0(); } private void CreateMeshForLOD_0() { //TODO: Die anderen grassorten (nicht nur index 0) in den submeshes generieren if (LOD_a == null) LOD_a = new Mesh(); LOD_a.Clear(); verts = new List(); indes = new List(); tipOffsets_missingInNextLOD = new List(); float myLodSize = basisChunkGroese * Mathf.Pow(2, myLOD); Vector2Int chunckPos = WorldToChunkPos(transform.position); if (grasFeld.allChunckData.TryGetValue(chunckPos, out var value)) { for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { for(int x =0; x < 3; x++) { Vector3 targetCreatePos = new Vector3(i * (basisChunkGroese / 16f), 0, j * (basisChunkGroese / 16f)) + new Vector3(Random.Range(-1f, 1f), 0, Random.Range(-1f, 1f)); byte currenPixelValue = value.Data[i * 16 + j][0]; if (currenPixelValue == 0) continue; verts.Add(targetCreatePos); tipOffsets_missingInNextLOD.Add(new Vector4(Random.Range(-1f, 1f), Random.Range(.7f, 1.4f), Random.Range(-1f, 1f), 0)); indes.Add(verts.Count - 1); } } } } LOD_a.vertices = verts.ToArray(); LOD_a.SetIndices(indes.ToArray(), MeshTopology.Points, 0); LOD_a.SetUVs(0, tipOffsets_missingInNextLOD); LOD_a.RecalculateBounds(); GetComponent().sharedMesh = LOD_a; GetComponent().sharedMaterial = materials[0]; var otherUV = new List(); otherUV.AddRange(tipOffsets_missingInNextLOD); int initCount = verts.Count / 2; for (int i = 0; i < initCount; i++) { int randomIndex = Random.Range(0, verts.Count - 1); verts.RemoveAt(randomIndex); otherUV.RemoveAt(randomIndex); tipOffsets_missingInNextLOD[randomIndex] += new Vector4(0,0,0,1f); } indes.Clear(); for (int i = 0; i < verts.Count - 1; i++) { indes.Add(i); } if (LOD_b == null) LOD_b = new Mesh(); LOD_b.vertices = verts.ToArray(); LOD_b.SetUVs(0, otherUV); LOD_b.SetIndices(indes.ToArray(), MeshTopology.Points, 0); LOD_b.RecalculateBounds(); } private Vector2Int WorldToChunkPos(Vector3 pos) { return new Vector2Int(Mathf.FloorToInt(pos.x / (int)basisChunkGroese) * (int)basisChunkGroese, Mathf.FloorToInt(pos.z / (int)basisChunkGroese) * (int)basisChunkGroese); } private Vector2Int InChunkFromLocalPos(Vector3 pos) { return new Vector2Int(Mathf.FloorToInt(pos.x / basisChunkGroese) * 15, Mathf.FloorToInt(pos.z / basisChunkGroese) * 15); } public void SetPosToGrid() { float myChunkSize = basisChunkGroese * Mathf.Pow(2f, myLOD); transform.position = new Vector3(Mathf.FloorToInt(transform.position.x / myChunkSize) * (int)myChunkSize ,0 ,Mathf.FloorToInt(transform.position.z / myChunkSize) * (int)myChunkSize); } public bool ContainsChunk(Vector2Int chunkPos) { float myChunkSize = basisChunkGroese * Mathf.Pow(2f, myLOD); Vector2Int gridChunkPos = new Vector2Int(Mathf.FloorToInt(chunkPos.x / myChunkSize) * (int)myChunkSize, Mathf.FloorToInt(chunkPos.y / myChunkSize) * (int)myChunkSize); Vector2Int myGridPos = new Vector2Int(Mathf.FloorToInt(transform.position.x / myChunkSize) * (int)myChunkSize, Mathf.FloorToInt(transform.position.z / myChunkSize) * (int)myChunkSize); if(myGridPos == gridChunkPos) return true; return false; } public bool ContainsChunk(Vector2Int chunkPos, Vector2Int pos, int lod) { float myChunkSize = basisChunkGroese * Mathf.Pow(2f, lod); Vector2Int gridChunkPos = new Vector2Int(Mathf.FloorToInt(chunkPos.x / myChunkSize) * (int)myChunkSize, Mathf.FloorToInt(chunkPos.y / myChunkSize) * (int)myChunkSize); Vector2Int myGridPos = new Vector2Int(Mathf.FloorToInt(pos.x / myChunkSize) * (int)myChunkSize, Mathf.FloorToInt(pos.y / myChunkSize) * (int)myChunkSize); if (myGridPos == gridChunkPos) return true; return false; } private void OnDrawGizmos() { return; Vector2Int chukPosInt = WorldToChunkPos(transform.position); if(false) for (int i = 0; i < 16; i++) for (int j = 0; j < 16; j++) { if(grasFeld.allChunckData != null && grasFeld.allChunckData.TryGetValue(chukPosInt, out var t)) { byte b = t.Data[i * 16 + j][0]; Gizmos.color = new Color(b, b, b); Gizmos.DrawCube(transform.position + new Vector3(i, 0, j) * (basisChunkGroese / 16f), Vector3.one * 0.1f); } } #if UNITY_EDITOR if(false) { Vector3 cameraPosition = SceneView.currentDrawingSceneView.camera.transform.position; float LODSize = basisChunkGroese * Mathf.Pow(2f, myLOD); Vector3 nearestPointToCamera = new Vector3( Mathf.Min(transform.position.x + LODSize, Mathf.Max(transform.position.x, cameraPosition.x)), Mathf.Min(transform.position.y + LODSize, Mathf.Max(transform.position.y, cameraPosition.y)), Mathf.Min(transform.position.z + LODSize, Mathf.Max(transform.position.z, cameraPosition.z)) ); Gizmos.color = Color.red; Gizmos.DrawCube(nearestPointToCamera, Vector3.one); } #endif //if (myLOD != 0) // return; float size = basisChunkGroese * Mathf.Pow(2, myLOD); Gizmos.color = Color.yellow; Gizmos.DrawWireCube(transform.position + Vector3.one * size / 2f, Vector3.one * size); if(Vector3.Distance(transform.position, Camera.current.transform.position) < 40) if(DebugGrasTextur != null) for(int i = 0; i < 16; i++) for(int j = 0; j < 16; j++) { var pixelColor = DebugGrasTextur.GetPixel(i, j); Gizmos.color = pixelColor; Gizmos.DrawCube(transform.position + new Vector3(i, 0, j) * (1f / 16f) * basisChunkGroese - new Vector3(4.75f, 0, 4.75f), Vector3.one * (1f / 16f)); } } }