Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Forked from stonstad/StarfieldMeshBuilder.cs
Created October 22, 2025 13:32
Show Gist options
  • Save unitycoder/5e5fe42d66e7ae904ca7587f2ecf1d85 to your computer and use it in GitHub Desktop.
Save unitycoder/5e5fe42d66e7ae904ca7587f2ecf1d85 to your computer and use it in GitHub Desktop.

Revisions

  1. @stonstad stonstad created this gist Oct 22, 2025.
    121 changes: 121 additions & 0 deletions StarfieldMeshBuilder.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,121 @@
    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<MeshFilter>();

    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);
    }
    }
    }
    }