]> git.tdb.fi Git - libs/gl.git/blob - source/sphere.cpp
Add a class for building icosahedron-based spheres
[libs/gl.git] / source / sphere.cpp
1 #include <cmath>
2 #include <vector>
3 #include "primitivebuilder.h"
4 #include "sphere.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace GL {
10
11 UvSphereBuilder::UvSphereBuilder(float r, unsigned s, unsigned n):
12         radius(r),
13         segments(s),
14         rings(n)
15 {
16         if(segments<3)
17                 segments = 3;
18
19         if(rings==0)
20                 rings = (segments+1)/2;
21         else if(rings<2)
22                 rings = 2;
23 }
24
25 void UvSphereBuilder::build(PrimitiveBuilder &builder) const
26 {
27         float u_scale = 1.0f/segments;
28         float v_scale = 1.0f/rings;
29         adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, radius*M_PI);
30
31         for(unsigned i=0; i<=rings; ++i)
32         {
33                 float av = i*M_PI/rings-M_PI/2;
34                 float cv = cos(av);
35                 float sv = sin(av);
36
37                 for(unsigned j=0; j<=segments; ++j)
38                 {
39                         float au = j*M_PI*2/segments;
40                         float cu = cos(au);
41                         float su = sin(au);
42
43                         builder.normal(cv*cu, cv*su, sv);
44                         builder.texcoord(j*u_scale, i*v_scale);
45                         if(generate_tbn)
46                         {
47                                 builder.tangent(-su, cu, 0);
48                                 builder.binormal(-sv*cu, -sv*su, cv);
49                         }
50                         builder.vertex(cv*cu*radius, cv*su*radius, sv*radius);
51                 }
52         }
53
54         builder.begin(TRIANGLES);
55         for(unsigned j=0; j<segments; ++j)
56         {
57                 builder.element(j);
58                 builder.element(segments+2+j);
59                 builder.element(segments+1+j);
60         }
61         unsigned top = (rings+1)*(segments+1)-1;
62         for(unsigned j=0; j<segments; ++j)
63         {
64                 builder.element(top-j);
65                 builder.element(top-(segments+2+j));
66                 builder.element(top-(segments+1+j));
67         }
68         builder.end();
69
70         for(unsigned i=1; i<rings; ++i)
71         {
72                 builder.begin(TRIANGLE_STRIP);
73                 unsigned base = i*(segments+1);
74                 for(unsigned j=0; j<=segments; ++j)
75                 {
76                         builder.element(base+segments+1+j);
77                         builder.element(base+j);
78                 }
79                 builder.end();
80         }
81 }
82
83
84 // https://en.wikipedia.org/wiki/Regular_icosahedron#Cartesian_coordinates
85 float IcoSphereBuilder::base_vertices[12*3] =
86 {
87          0.0f,       -0.5257311f, -0.8506508f,
88          0.0f,        0.5257311f, -0.8506508f,
89          0.0f,       -0.5257311f,  0.8506508f,
90          0.0f,        0.5257311f,  0.8506508f,
91         -0.8506508f,  0.0f,       -0.5257311f,
92         -0.8506508f,  0.0f,        0.5257311f,
93          0.8506508f,  0.0f,       -0.5257311f,
94          0.8506508f,  0.0f,        0.5257311f,
95         -0.5257311f, -0.8506508f,  0.0f,
96          0.5257311f, -0.8506508f,  0.0f,
97         -0.5257311f,  0.8506508f,  0.0f,
98          0.5257311f,  0.8506508f,  0.0f
99 };
100
101 unsigned IcoSphereBuilder::base_triangles[20*3] =
102 {
103         0, 1, 6,
104         1, 0, 4,
105         2, 3, 5,
106         3, 2, 7,
107         4, 5, 10,
108         5, 4, 8,
109         6, 7, 9,
110         7, 6, 11,
111         8, 9, 2,
112         9, 8, 0,
113         10, 11, 1,
114         11, 10, 3,
115         0, 8, 4,
116         0, 6, 9,
117         1, 4, 10,
118         1, 11, 6,
119         2, 5, 8,
120         2, 9, 7,
121         3, 10, 5,
122         3, 7, 11
123 };
124
125 unsigned IcoSphereBuilder::base_edges[30*2] = { 0, 0 };
126 unsigned IcoSphereBuilder::base_tri_edges[20*3] = { 0, 0, 0 };
127
128 IcoSphereBuilder::IcoSphereBuilder(float r, unsigned s):
129         radius(r),
130         subdivision(s)
131 {
132         if(base_edges[0]==base_edges[1])
133                 initialize_edges();
134 }
135
136 void IcoSphereBuilder::initialize_edges()
137 {
138         vector<int> edge_map(12*12, -1);
139         unsigned next_edge = 0;
140         for(unsigned i=0; i<20; ++i)
141                 for(unsigned j=0; j<3; ++j)
142                 {
143                         unsigned v1 = base_triangles[i*3+j];
144                         unsigned v2 = base_triangles[i*3+(j+1)%3];
145                         int e = edge_map[v1*12+v2];
146                         if(e<0)
147                         {
148                                 e = next_edge++;
149                                 base_edges[e*2] = v1;
150                                 base_edges[e*2+1] = v2;
151                                 // The other triangle using this edge will have the vertices swapped
152                                 edge_map[v2*12+v1] = e|32;
153                         }
154                         base_tri_edges[i*3+j] = e;
155                 }
156 }
157
158 void IcoSphereBuilder::build(PrimitiveBuilder &bld) const
159 {
160         for(unsigned i=0; i<12; ++i)
161         {
162                 const float *v = base_vertices+i*3;
163                 bld.normal(v[0], v[1], v[2]);
164                 bld.vertex(v[0]*radius, v[1]*radius, v[2]*radius);
165         }
166
167         if(subdivision>1)
168         {
169                 vector<Vector3> edge_subdiv(30*(subdivision+1));
170                 for(unsigned i=0; i<30; ++i)
171                 {
172                         Vector3 v1(base_vertices+base_edges[i*2]*3);
173                         Vector3 v2(base_vertices+base_edges[i*2+1]*3);
174                         for(unsigned j=1; j<subdivision; ++j)
175                         {
176                                 float t = static_cast<float>(j)/subdivision;
177                                 Vector3 v = v1*(1.0f-t)+v2*t;
178                                 edge_subdiv[i*(subdivision-1)+j-1] = v;
179                                 v.normalize();
180                                 bld.normal(v);
181                                 bld.vertex(v*radius);
182                         }
183                 }
184
185                 for(unsigned i=0; i<20; ++i)
186                         for(unsigned j=1; j<subdivision; ++j)
187                         {
188                                 unsigned e = base_tri_edges[i*3];
189                                 Vector3 v1 = edge_subdiv[edge_vertex(e, subdivision-j)];
190                                 e = base_tri_edges[i*3+1];
191                                 Vector3 v2 = edge_subdiv[edge_vertex(e, j)];
192                                 for(unsigned k=1; k<j; ++k)
193                                 {
194                                         float t = static_cast<float>(k)/j;
195                                         Vector3 v = normalize(v1*(1.0f-t)+v2*t);
196                                         bld.normal(v);
197                                         bld.vertex(v*radius);
198                                 }
199                         }
200
201                 for(unsigned i=0; i<20; ++i)
202                 {
203                         unsigned mid = 12+30*(subdivision-1)+i*(subdivision-1)*(subdivision-2)/2;
204                         for(unsigned j=1; j<=subdivision; ++j)
205                         {
206                                 bld.begin(TRIANGLE_STRIP);
207                                 if(j==subdivision)
208                                         bld.element(base_triangles[i*3]);
209                                 else
210                                         bld.element(12+edge_vertex(base_tri_edges[i*3], subdivision-j));
211
212                                 if(j==1)
213                                 {
214                                         bld.element(base_triangles[i*3+1]);
215                                         bld.element(12+edge_vertex(base_tri_edges[i*3+1], j));
216                                 }
217                                 else
218                                 {
219                                         bld.element(12+edge_vertex(base_tri_edges[i*3], subdivision-j+1));
220
221                                         if(j==subdivision)
222                                                 bld.element(12+edge_vertex(base_tri_edges[i*3+2], subdivision-1));
223                                         else
224                                                 bld.element(mid+(j-1)*(j-2)/2);
225
226                                         for(unsigned k=2; k<j; ++k)
227                                         {
228                                                 bld.element(mid+(j-2)*(j-3)/2+k-2);
229                                                 if(j==subdivision)
230                                                         bld.element(12+edge_vertex(base_tri_edges[i*3+2], subdivision-k));
231                                                 else
232                                                         bld.element(mid+(j-1)*(j-2)/2+k-1);
233                                         }
234
235                                         bld.element(12+edge_vertex(base_tri_edges[i*3+1], j-1));
236                                         if(j==subdivision)
237                                                 bld.element(base_triangles[i*3+2]);
238                                         else
239                                                 bld.element(12+edge_vertex(base_tri_edges[i*3+1], j));
240                                 }
241                                 bld.end();
242                         }
243                 }
244         }
245         else
246         {
247                 bld.begin(TRIANGLES);
248                 for(unsigned i=0; i<20*3; ++i)
249                         bld.element(base_triangles[i]);
250                 bld.end();
251         }
252 }
253
254 unsigned IcoSphereBuilder::edge_vertex(unsigned e, unsigned i) const
255 {
256         return (e&31)*(subdivision-1)+((e&32)?subdivision-i:i)-1;
257 }
258
259 } // namespace GL
260 } // namespace Msp