Unityでプロシージャルモデリング (トーラス)

Unityでメッシュをスクリプトでプロシージャルに生成する方法について何回かに分けて書いていきます。

* [第1回 Unityでプロシージャルモデリング \(三角形・四角形・正多角形\) \- Qiita](https://qiita.com/aa_debdeb/items/d34bd733e0584494bdbd)
* [第2回 Unityでプロシージャルモデリング \(平面\) \- Qiita](https://qiita.com/aa_debdeb/items/043695f998606876cfb4)
* [第3回 Unityでプロシージャルモデリング \(直方体\) \- Qiita](https://qiita.com/aa_debdeb/items/983c264879084bd40b0c)
* [第4回 Unityでプロシージャルモデリング \(円柱\) \- Qiita](https://qiita.com/aa_debdeb/items/c54b14696363873c640d)
* [第5回 Unityでプロシージャルモデリング \(球\) \- Qiita](https://qiita.com/aa_debdeb/items/cc46409b99301a5e34c0)
* 第6回 Unityでプロシージャルモデリング (トーラス)

今回はトーラスです。

![torus_01.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/e03f240c-a77b-d97a-f794-59d22f368437.png)


```csharp:Torus.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Torus : MonoBehaviour
{
    [SerializeField] private float majorRadius = 3f;
    [SerializeField] private float minorRadius = 1f;
    [SerializeField] private int thetaSegments = 8;
    [SerializeField] private int phiSegments = 8;

    private void Awake()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        Vector3[] vertices = new Vector3[thetaSegments * phiSegments];
        int[] triangles = new int[thetaSegments * phiSegments * 2 * 3];

        float thetaStep = Mathf.PI * 2.0f / thetaSegments;
        float phiStep = Mathf.PI * 2.0f / phiSegments;
        int vi = 0;
        for (int hi = 0; hi < thetaSegments; hi++)
        {
            Quaternion rot = Quaternion.AngleAxis(hi * thetaStep * Mathf.Rad2Deg, Vector3.up);
            for (int pi = 0; pi < phiSegments; pi++)
            {
                float phi = pi * phiStep;
                vertices[vi++] = rot * new Vector3(majorRadius + minorRadius * Mathf.Cos(phi), minorRadius * Mathf.Sin(phi), 0);
            }
        }

        int ti = 0;
        for (int hi = 0; hi < thetaSegments; hi++)
        {
            int hj = hi != thetaSegments - 1 ? hi + 1 : 0; 
            for (int pi = 0; pi < phiSegments; pi++)
            {
                int pj = pi != phiSegments - 1 ? pi + 1 : 0;
                int v00 = pi + hi * phiSegments;
                int v10 = pj + hi * phiSegments;
                int v01 = pi + hj * phiSegments;
                int v11 = pj + hj * phiSegments;
                ti = MakeQuad(triangles, ti, v00, v10, v01, v11);
            }
        }

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }

    private int MakeQuad(int[] triangles, int ti, int v00, int v10, int v01, int v11)
    {
        triangles[ti] = v00;
        triangles[ti + 1] = triangles[ti + 5] = v01;
        triangles[ti + 2] = triangles[ti + 4] = v10;
        triangles[ti + 3] = v11;
        return ti + 6;
    }
}
```

`majorRadius`でドーナッツの輪っかの大きさを、`minorRadius`でドーナッツの太さを指定できます。また、`thetaSegments`が輪っかの分割数を、`phiSegments`で太さ部分の分割数を指定します。

メッシュの頂点と三角形の以下のように構成されています。
頂点位置を直接計算するのは難しいので一度xy平面上に配置してから、クォータニオンでy軸に対して回転させることで適切な値になるようにしています。

![torus_02.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/d1c52c82-ddd4-d3fe-b712-2e5b4fe3bbfc.png)

トーラスは底面と上面がつながった円柱だとみなすことができるので、三角形の構築は円柱と同じです。円柱のメッシュについては[以前の記事](https://qiita.com/aa_debdeb/items/c54b14696363873c640d)で解説しましたので参考にしてください。

![torus_ex.png](https://qiita-image-store.s3.amazonaws.com/0/61569/d3b99682-2c72-4e85-8d8b-d484ca170420.png)