1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//Store each of the vertex information in a triangle
//각 삼각형의 정점 인덱스 정보를 저장
public struct TrianglePoly
{
    public int triA, triB, triC;
 
    public TrianglePoly(int triA, int triB, int triC)
    {
        this.triA = triA;
        this.triB = triB;
        this.triC = triC;
    }
}
 
//Store the outer coordinates of each planet piece
//행성을 구성하는 조각들의 정점 좌표 저장
public struct OutlineVector
{
    public Vector3 triA, triB, triC;
 
    public OutlineVector(Vector3 triA, Vector3 triB, Vector3 triC)
    {
        this.triA = triA;
        this.triB = triB;
        this.triC = triC;
    }
}
 
public class GenerateIcosphere : MonoBehaviour {
     /*
     * This script records the creation of Icosphere-based mesh information
     * and the range position information for each piece
     */
 
    protected List<List<TrianglePoly>> generateSurface;
    protected List<TrianglePoly> trianglePoly;
    protected List<Vector3> vertexes;
    protected List<OutlineVector> outlinePositions;
 
    //Create a Icosahedron
    //가장 기본이 되는 정 20면체를 생성합니다.
    protected void InitIcosahedron()
    {
        trianglePoly = new List<TrianglePoly>();
        vertexes = new List<Vector3>();
        
        //20면체의 기준이 되는 3개의 직사각형 길이 r (=구의 지름)
        float r = (1f + Mathf.Sqrt(5f)) / 2f;
 
        vertexes.Add(new Vector3(-1f, r, 0f).normalized);
        vertexes.Add(new Vector3(1f, r, 0f).normalized);
        vertexes.Add(new Vector3(-1f, -r, 0f).normalized);
        vertexes.Add(new Vector3(1f, -r, 0f).normalized);
        vertexes.Add(new Vector3(0f, -1f, r).normalized);
        vertexes.Add(new Vector3(0f, 1f, r).normalized);
        vertexes.Add(new Vector3(0f, -1f, -r).normalized);
        vertexes.Add(new Vector3(0f, 1f, -r).normalized);
        vertexes.Add(new Vector3(r, 0f, -1f).normalized);
        vertexes.Add(new Vector3(r, 0f, 1f).normalized);
        vertexes.Add(new Vector3(-r, 0f, -1f).normalized);
        vertexes.Add(new Vector3(-r, 0f, 1f).normalized);
 
        trianglePoly.Add(new TrianglePoly(0115));
        trianglePoly.Add(new TrianglePoly(051));
        trianglePoly.Add(new TrianglePoly(017));
        trianglePoly.Add(new TrianglePoly(0710));
        trianglePoly.Add(new TrianglePoly(01011));
        trianglePoly.Add(new TrianglePoly(159));
        trianglePoly.Add(new TrianglePoly(5114));
        trianglePoly.Add(new TrianglePoly(11102));
        trianglePoly.Add(new TrianglePoly(1076));
        trianglePoly.Add(new TrianglePoly(718));
        trianglePoly.Add(new TrianglePoly(394));
        trianglePoly.Add(new TrianglePoly(342));
        trianglePoly.Add(new TrianglePoly(326));
        trianglePoly.Add(new TrianglePoly(368));
        trianglePoly.Add(new TrianglePoly(389));
        trianglePoly.Add(new TrianglePoly(495));
        trianglePoly.Add(new TrianglePoly(2411));
        trianglePoly.Add(new TrianglePoly(6210));
        trianglePoly.Add(new TrianglePoly(867));
        trianglePoly.Add(new TrianglePoly(981));
    }
 
    //Divide each side of the triangle in half
    //삼각형을 반으로 나눕니다.
    private int GetMiddlePointVertexesIndex(Dictionary<intint> cache, int key1, int key2)
    {
        int retVerticeIndex;
 
        //인덱스 키 생성(높은 인덱스 값(상위 16비트) + 낮은 인덱스 값(하위 16비트))를 생성
        int sKey = Mathf.Min(key1, key2);
        int lKey = Mathf.Max(key1, key2);
        int key = (sKey << 16+ lKey;
        
        //생성된 인덱스 키를 검색하여 이미 가지고 있는 값이라면 반환하여 연산을 줄여줍니다.
        if (cache.TryGetValue(key, out retVerticeIndex))
        {
            return retVerticeIndex;
        }
 
        Vector3 vPoint1 = vertexes[key1];
        Vector3 vPoint2 = vertexes[key2];
        Vector3 middlePoint = Vector3.Lerp(vPoint1, vPoint2, .5f).normalized;
 
        retVerticeIndex = vertexes.Count;
        vertexes.Add(middlePoint);
 
        cache.Add(key, retVerticeIndex);
 
        return retVerticeIndex;
    }
 
    //Distribute each triangle as much detail as the desired sphere,
    //and position each planet piece by the range
    //divCount : sphere detail, generateMapRange : planet piece by the range
    //사용자가 원하는 만큼 매끄러운 구체를 만듭니다. 또한 한 조각의 크기 또한 정합니다.
    protected void SubdividedTriangles(int divCount, int generateMapRange)
    {
        generateSurface = new List<List<TrianglePoly>>();
        outlinePositions = new List<OutlineVector>();
        Dictionary<intint> midPointCache = new Dictionary<intint>();
 
        for (int i = 0; i <= divCount; i++)
        {
            //원하는 조각 크기가 될 때까지 단순 분할을 수행합니다.
            if (i <= generateMapRange)
            {
                List<TrianglePoly> newTriPoly = new List<TrianglePoly>();
 
                foreach (TrianglePoly tri in trianglePoly)
                {
                    int a = tri.triA;
                    int b = tri.triB;
                    int c = tri.triC;
 
                    int ab = GetMiddlePointVertexesIndex(midPointCache, a, b);
                    int bc = GetMiddlePointVertexesIndex(midPointCache, b, c);
                    int ca = GetMiddlePointVertexesIndex(midPointCache, c, a);
 
                    newTriPoly.Add(new TrianglePoly(a, ab, ca));
                    newTriPoly.Add(new TrianglePoly(b, bc, ab));
                    newTriPoly.Add(new TrianglePoly(c, ca, bc));
                    newTriPoly.Add(new TrianglePoly(ab, bc, ca));
                }
                //원하는 조각 범위에 도달하면 각 조각 정점 정보를 저장합니다.
                if (i == generateMapRange)
                {
                    foreach (TrianglePoly tri in newTriPoly)
                    {
                        List<TrianglePoly> newSurface = new List<TrianglePoly>();
                        newSurface.Add(tri);
                        generateSurface.Add(newSurface);
                        outlinePositions.Add(new OutlineVector(
                            vertexes[tri.triA],
                            vertexes[tri.triB],
                            vertexes[tri.triC]));
                    }
                }
                else
                {
                    //나눠진 조각 정보를 갱신합니다.
                    trianglePoly = newTriPoly;
                }
            }
            //각 행성 조각이 결정된 이후에는 조각마다 분할 연산을 수행합니다.
            else
            {
                List<List<TrianglePoly>> newGenerateSurface = new List<List<TrianglePoly>>();
 
                foreach (List<TrianglePoly> polygon in generateSurface)
                {
                    List<TrianglePoly> newPoly = new List<TrianglePoly>();
 
                    foreach (TrianglePoly tri in polygon)
                    {
                        int a = tri.triA;
                        int b = tri.triB;
                        int c = tri.triC;
 
                        int ab = GetMiddlePointVertexesIndex(midPointCache, a, b);
                        int bc = GetMiddlePointVertexesIndex(midPointCache, b, c);
                        int ca = GetMiddlePointVertexesIndex(midPointCache, c, a);
 
                        newPoly.Add(new TrianglePoly(a, ab, ca));
                        newPoly.Add(new TrianglePoly(b, bc, ab));
                        newPoly.Add(new TrianglePoly(c, ca, bc));
                        newPoly.Add(new TrianglePoly(ab, bc, ca));
                    }
                    newGenerateSurface.Add(newPoly);
                }
                generateSurface = newGenerateSurface;
            }
        }
    }
}
cs

지오데식 구란 길이가 일정한 정삼각형으로만 이루어진 구형을 말합니다.

원리는 정 20면체로 이루어진 가장 기본적인 지오데식 구를 생성하여, 각 삼각형을 나눔으로서 구형에 가까운 매끈한 표면을 만드는 것입니다.

제일 긴 부분의 길이가 (1f + Mathf.Sqrt(5f)) / 2f  인 직사각형 3개를  그림과 같이 배치합니다. 그떄 각 꼭지점을 이으면 정 20면체를 만들 수 있습니다.

(또는 정 팔면체의 꼭지점을 황금비로 나누어도 20면체의 각 버텍스 정보를 알 수 있습니다)

그 후 각 면을 이루는 정삼각형을 계속 반으로 나누면, 움짤과 같이 매끄한 구형을 생성할 수 있습니다. 

 

Posted by Haedo
,