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