private static ushort GetMiddlePoint(ushort a, ushort b,
                                         MeshTool geometry,
                                         float radius,
                                         Dictionary <int, ushort> edges)
    {
        ushort i;

        int min = a < b ? a : b;
        int max = a < b ? b : a;
        int key = (min << 16) | max;

        if (!edges.TryGetValue(key, out i))
        {
            Vector3 middle = FasterMath.Mul(FasterMath.Add(geometry.positions[a],
                                                           geometry.positions[b]),
                                            0.5f);
            middle.Normalize();

            i = (ushort)geometry.ActiveVertices;
            geometry.ActiveVertices += 1;

            geometry.positions[i] = FasterMath.Mul(middle, radius);
            geometry.normals[i]   = middle;

            edges[key] = i;
        }

        return(i);
    }
    public static void HemisphereOld(MeshTool geometry,
                                     float radius,
                                     int complexity,
                                     bool correction)
    {
        int subdivisions = complexity / 6;
        int sides        = (complexity % 6) + 3;

        if (correction)
        {
            // try to make the average of apoth/radius = desired radius
            // * desired = (radius + apoth) / 2
            // apoth = radius * cos(pi / sides)
            // * desired = (radius + radius * cos(pi / sides)) / 2
            // * desired = radius * (1 + cos(pi / sides)) / 2
            // rearrange to determine radius from desired radius
            // * radius = desired / ((1 + cos(pi / sides)) / 2)

            radius = radius * 2 / (1 + Mathf.Cos(Mathf.PI / (sides * (subdivisions + 1))));
        }

        // pyramid
        int vertCount = sides + 1;
        int faceCount = sides;
        int edgeCount = sides * 2;

        for (int i = 0; i < subdivisions; ++i)
        {
            vertCount += edgeCount;
            edgeCount += faceCount * 6;
            faceCount *= 4;
        }

        geometry.SetVertexCount(vertCount);
        geometry.SetIndexCount(faceCount * 3, lazy: true);

        geometry.ActiveVertices = sides + 1;
        geometry.ActiveIndices  = sides * 3;

        geometry.positions[0] = new Vector3(0, radius, 0);
        geometry.normals[0]   = new Vector3(0, 1, 0);

        float da = Mathf.PI * 2 / sides;

        for (int i = 1; i < vertCount; ++i)
        {
            var normal = new Vector3(Mathf.Cos(da * i), 0, Mathf.Sin(da * i));

            geometry.positions[i] = FasterMath.Mul(normal, radius);
            geometry.normals[i]   = normal;
        }

        for (int i = 1; i < sides; ++i)
        {
            geometry.SetTriangle(i, 0, i + 1, i);
        }

        geometry.SetTriangle(0, 0, 1, sides);

        int prevTriCount = geometry.ActiveIndices / 3;
        int nextTriCount = 0;

        var edges = new Dictionary <int, ushort>(prevTriCount * 6);

        for (int i = 0; i < subdivisions; i++)
        {
            nextTriCount = prevTriCount * 4;

            for (int j = 0; j < prevTriCount; ++j)
            {
                var tri = geometry.GetTriangle(j);

                int a = GetMiddlePoint((ushort)tri.x, (ushort)tri.y, geometry, radius, edges);
                int b = GetMiddlePoint((ushort)tri.y, (ushort)tri.z, geometry, radius, edges);
                int c = GetMiddlePoint((ushort)tri.z, (ushort)tri.x, geometry, radius, edges);

                geometry.SetTriangle(prevTriCount + j * 3 + 0, tri.x, a, c);
                geometry.SetTriangle(prevTriCount + j * 3 + 1, tri.y, b, a);
                geometry.SetTriangle(prevTriCount + j * 3 + 2, tri.z, c, b);
                geometry.SetTriangle(j, a, b, c);
            }

            prevTriCount = nextTriCount;
            edges.Clear();
        }

        geometry.mesh.Clear();
        geometry.Apply(positions: true, normals: true, indices: true);
    }