using System.Collections.Generic; using UnityEngine; namespace Terrain { public class Clouds : MonoBehaviour { public int cloudHeight = 100; [SerializeField] private Texture2D cloudPattern; [SerializeField] private Material cloudMaterial; [SerializeField] private World world; private bool[,] _cloudData; private int _cloudTextureWidth; private int _cloudTileSize; private Vector3Int _offset; private readonly Dictionary _clouds = new Dictionary(); private void Start() { if (World.Settings.clouds == 0) return; _cloudTextureWidth = cloudPattern.width; _cloudTileSize = WorldData.ChunkSize.x; _offset = new Vector3Int(-(_cloudTextureWidth / 2), 0, -(_cloudTextureWidth / 2)); transform.position = new Vector3(WorldData.WorldCenter, cloudHeight, WorldData.WorldCenter); LoadCloudData(); CreateClouds(); } private void LoadCloudData() { _cloudData = new bool[_cloudTextureWidth, _cloudTextureWidth]; Color[] colors = cloudPattern.GetPixels(); for (int x = 0; x < _cloudTextureWidth; x++) { for (int y = 0; y < _cloudTextureWidth; y++) { _cloudData[x, y] = colors[x + y * _cloudTextureWidth].a > 0; } } } private void CreateClouds() { for (int x = 0; x < _cloudTextureWidth; x += _cloudTileSize) for (int y = 0; y < _cloudTextureWidth; y += _cloudTileSize) { Mesh cloudMesh = World.Settings.clouds == 1 ? CreateFastCloudMesh(x, y) : CreateFancyCloudMesh(x, y); Vector3 position = new Vector3(x, cloudHeight, y); _clouds.Add(CloudTilePosFromVector3(position), CreateCloudTile(cloudMesh, position)); } } public void UpdateClouds() { if (World.Settings.clouds == 0) return; for (int x = 0; x < _cloudTextureWidth; x += _cloudTileSize) for (int y = 0; y < _cloudTextureWidth; y += _cloudTileSize) { Vector3 position = world.player.position + new Vector3(x, 0, y) + _offset; position = new Vector3(RoundToCloud(position.x), cloudHeight, RoundToCloud(position.z)); Vector2Int cloudPostition = CloudTilePosFromVector3(position); _clouds[cloudPostition].transform.position = position; } } private int RoundToCloud(float value) => Mathf.FloorToInt(value / _cloudTileSize) * _cloudTileSize; private Mesh CreateFastCloudMesh(int x, int y) { List vertices = new List(); List triangles = new List(); List normals = new List(); int vertexCount = 0; for (int xIncrement = 0; xIncrement < _cloudTileSize; xIncrement++) { for (int yIncrement = 0; yIncrement < _cloudTileSize; yIncrement++) { int xVal = x + xIncrement; int yVal = y + yIncrement; if (!_cloudData[xVal, yVal]) continue; vertices.Add(new Vector3(xIncrement, 0, yIncrement)); vertices.Add(new Vector3(xIncrement, 0, yIncrement + 1)); vertices.Add(new Vector3(xIncrement + 1, 0, yIncrement + 1)); vertices.Add(new Vector3(xIncrement + 1, 0, yIncrement)); for (int i = 0; i < 4; i++) normals.Add(Vector3.down); triangles.Add(vertexCount + 1); triangles.Add(vertexCount); triangles.Add(vertexCount + 2); triangles.Add(vertexCount + 2); triangles.Add(vertexCount); triangles.Add(vertexCount + 3); vertexCount += 4; } } Mesh mesh = new Mesh(); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.normals = normals.ToArray(); return mesh; } private Mesh CreateFancyCloudMesh(int x, int y) { List vertices = new List(); List triangles = new List(); List normals = new List(); int vertexCount = 0; for (int xIncrement = 0; xIncrement < _cloudTileSize; xIncrement++) { for (int yIncrement = 0; yIncrement < _cloudTileSize; yIncrement++) { int xVal = x + xIncrement; int yVal = y + yIncrement; if (!_cloudData[xVal, yVal]) continue; for (int i = 0; i < 6; i++) { if (CheckCloudData(new Vector3Int(xVal, 0, yVal) + WorldData.FaceChecks[i])) continue; for (int j = 0; j < 4; j++) { Vector3 vert = new Vector3(xIncrement, 0, yIncrement); vert += WorldData.BlockVerts[WorldData.BlockTris[i, j]]; vertices.Add(vert); } for (int j = 0; j < 4; j++) normals.Add(WorldData.FaceChecks[j]); triangles.Add(vertexCount); triangles.Add(vertexCount + 1); triangles.Add(vertexCount + 2); triangles.Add(vertexCount + 2); triangles.Add(vertexCount + 1); triangles.Add(vertexCount + 3); vertexCount += 4; } } } Mesh mesh = new Mesh(); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.normals = normals.ToArray(); return mesh; } private bool CheckCloudData(Vector3Int faceCheck) { if (faceCheck.y != 0) return false; Vector2Int pos = CloudTilePosFromVector3(faceCheck); return _cloudData[pos.x, pos.y]; } private GameObject CreateCloudTile(in Mesh mesh, in Vector3 position) { GameObject cloudTile = new GameObject(); cloudTile.transform.position = new Vector3(position.x, position.y, position.z); cloudTile.transform.parent = transform; cloudTile.name = $"CloudTile [{position.x}:{position.z}]"; MeshFilter meshFilter = cloudTile.AddComponent(); MeshRenderer meshRenderer = cloudTile.AddComponent(); meshRenderer.material = cloudMaterial; meshFilter.mesh = mesh; return cloudTile; } private Vector2Int CloudTilePosFromVector3(in Vector3 pos) => new Vector2Int(CloudTileCoordFromFloat(pos.x), CloudTileCoordFromFloat(pos.z)); private int CloudTileCoordFromFloat(in float coord) { float a = coord / _cloudTextureWidth; a -= Mathf.FloorToInt(a); int b = Mathf.FloorToInt(_cloudTextureWidth * a); return b; } } }