using System; using UnityEngine; using UnityEngine.Rendering; using Random = System.Random; namespace StellarConquest.Presentation.Unity { public class StarfieldMeshBuilder : MonoBehaviour { public float SphereRadius = 4000; public int StarsCount = 0; public int Seed = 0; [Range(1, 15)] public float StarSizeScale = 4.5f; [SerializeField] AnimationCurve StarSize = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 10)); [SerializeField] AnimationCurve Brightness = new AnimationCurve(new Keyframe(0, 0.8f), new Keyframe(1, 2f)); [SerializeField] Gradient StarColor = new Gradient(); private MeshFilter _MeshFilter; private Mesh _Mesh; private bool _IsDirty = false; #if UNITY_EDITOR private void OnValidate() { _IsDirty = true; } #endif private void Update() { if (_IsDirty) Build(); } [ContextMenu("Build")] public void Build() { _IsDirty = false; if (!_MeshFilter) _MeshFilter = GetComponent(); if (_Mesh == null) { _Mesh = new Mesh() { indexFormat = IndexFormat.UInt32 }; _Mesh.name = name; _MeshFilter.mesh = _Mesh; } _Mesh.Clear(); Vector3[] vertices = new Vector3[4 * StarsCount]; int[] triangles = new int[3 * 2 * StarsCount]; Vector2[] uvs = new Vector2[vertices.Length]; Color[] colors = new Color[vertices.Length]; Random rnd = new Random(Seed); for (int i = 0; i < StarsCount; i++) { //calc position int index = i * 4; float distance = SphereRadius + SphereRadius * NextFloat() / 2f; float size = StarSize.Evaluate(NextFloat()) * StarSizeScale; Vector3 pos = NextOnSphere() * distance; Vector3 up = Vector3.Cross(pos, Math.Abs(pos.z) > distance / 2 ? Vector3.up : Vector3.right).normalized; Vector3 left = Vector3.Cross(-up, pos).normalized; //vertices vertices[index + 0] = pos + (left + up) * size; vertices[index + 1] = pos + (-left + up) * size; vertices[index + 2] = pos + (-left - up) * size; vertices[index + 3] = pos + (left - up) * size; //uv uvs[index + 0] = new Vector2(0, 0); uvs[index + 1] = new Vector2(1, 0); uvs[index + 2] = new Vector2(1, 1); uvs[index + 3] = new Vector2(0, 1); //triangles var iTr = i * 2 * 3; triangles[iTr++] = index + 0; triangles[iTr++] = index + 1; triangles[iTr++] = index + 2; triangles[iTr++] = index + 0; triangles[iTr++] = index + 2; triangles[iTr++] = index + 3; //color Color color = Brightness.Evaluate(NextFloat()) * StarColor.Evaluate(NextFloat()); colors[index + 0] = colors[index + 1] = colors[index + 2] = colors[index + 3] = color; } _Mesh.SetVertices(vertices); _Mesh.SetUVs(0, uvs); _Mesh.SetTriangles(triangles, 0); _Mesh.SetColors(colors); _Mesh.RecalculateNormals(); _Mesh.RecalculateBounds(); float NextFloat() { return (float)rnd.NextDouble(); } Vector3 NextOnSphere() { var theta = 2 * Math.PI * rnd.NextDouble(); var phi = Math.Acos(2 * rnd.NextDouble() - 1.0); var sinPhi = Math.Sin(phi); var x = Math.Cos(theta) * sinPhi; var y = Math.Sin(theta) * sinPhi; var z = Math.Cos(phi); return new Vector3((float)x, (float)y, (float)z); } } } }