Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Procedural circle mesh with uniform faces

I'm trying to create a 2d circle procedurally with uniform faces like so.

enter image description here

Normally, I would create it with a triangle fan structure, but I need faces to be roughly identical. I looked for examples, but I could only find "cube to sphere" examples. A compromise could be something similar to this :

enter image description here

Could you help me finding a way to draw this structure? I'd like to do it in C# but js or even pseudo code would do!

Thanks a lot

like image 208
JFK Avatar asked Nov 21 '18 06:11

JFK


1 Answers

You got me interested with your question, and I think I've got the solution you were looking for. Here is how we can create a topology that you desired:

1) We start with a hexagon. Why hexagon and not other shape? Because hexagon is the only magic shape with its radius equal too the length of its side. We will call this radius R. We will now try to create a shape that resembles circle and is made of triangles with side length approximately R.

2) Now imagine some concentric circles, with radius R, 2R, 3R and so on - the more, the higher is the resolution.

3) Circle number 1 has radius R. We will now replace that circle with a hexagon with radius R.

Example 1

4) We will now add more nodes on second circle to expand our hexagon. What is the circumference of circle number N? It is 2PiRN. Now we want to divide it into X edges of length approximately R. Hence X=2PiN, which is approximately 6N. So we will divide first circle into 6 edges (hexagon), second one into 12, then 18, 24 and so on.

5) Now we have lots of circles divided into edges. We now need to connect edges into triangles. How do we build triangles between circle N (outer) and N-1 (inner)? Outer circle has 6 more edges than the inner one. If they had identical number of vertices, we could connect them with quads. But they don't. So, we will still try to build quads, but for each N quads we build, we will need to add 1 triangle. Each quad uses 2 vertices from inner and 2 vertices from outer circle. Each triangle uses 2 vertices from the outer circle and only 1 from inner, thus compensating the excess of vertices.

Example 2

6) And now at last, there is some tested sample code that does what you need. It will generate a circle with uniform topology, with center point at origin and radius of 1, divided into *resolution sub circles. It could use some minor performance optimization (that's out of scope for now), but all in all it should do the job.

using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshFilter))]
public class UniformCirclePlane : MonoBehaviour {

    public int resolution = 4;

    // Use this for initialization
    void Start() {
        GetComponent<MeshFilter>().mesh = GenerateCircle(resolution);
    }

    // Update is called once per frame
    void Update() {

    }

    // Get the index of point number 'x' in circle number 'c'
    static int GetPointIndex(int c, int x) {
        if (c < 0) return 0; // In case of center point
        x = x % ((c + 1) * 6); // Make the point index circular
                               // Explanation: index = number of points in previous circles + central point + x
                               // hence: (0+1+2+...+c)*6+x+1 = ((c/2)*(c+1))*6+x+1 = 3*c*(c+1)+x+1

        return (3 * c * (c + 1) + x + 1);
    }

    public static Mesh GenerateCircle(int res) {

        float d = 1f / res;

        var vtc = new List<Vector3>();
        vtc.Add(Vector3.zero); // Start with only center point
        var tris = new List<int>();

        // First pass => build vertices
        for (int circ = 0; circ < res; ++circ) {
            float angleStep = (Mathf.PI * 2f) / ((circ + 1) * 6);
            for (int point = 0; point < (circ + 1) * 6; ++point) {
                vtc.Add(new Vector2(
                    Mathf.Cos(angleStep * point),
                    Mathf.Sin(angleStep * point)) * d * (circ + 1));
            }
        }

        // Second pass => connect vertices into triangles
        for (int circ = 0; circ < res; ++circ) {
            for (int point = 0, other = 0; point < (circ + 1) * 6; ++point) {
                if (point % (circ + 1) != 0) {
                    // Create 2 triangles
                    tris.Add(GetPointIndex(circ - 1, other + 1));
                    tris.Add(GetPointIndex(circ - 1, other));
                    tris.Add(GetPointIndex(circ, point));
                    tris.Add(GetPointIndex(circ, point));
                    tris.Add(GetPointIndex(circ, point + 1));
                    tris.Add(GetPointIndex(circ - 1, other + 1));
                    ++other;
                } else {
                    // Create 1 inverse triange
                    tris.Add(GetPointIndex(circ, point));
                    tris.Add(GetPointIndex(circ, point + 1));
                    tris.Add(GetPointIndex(circ - 1, other));
                    // Do not move to the next point in the smaller circle
                }
            }
        }

        // Create the mesh
        var m = new Mesh();
        m.SetVertices(vtc);
        m.SetTriangles(tris, 0);
        m.RecalculateNormals();
        m.UploadMeshData(true);

        return m;

    }
}

Final Result:

enter image description here

like image 196
Yuri Nudelman Avatar answered Oct 31 '22 04:10

Yuri Nudelman