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