1 /**********************************************************************
2 Sphere - Class for drawing spheres in OpenGL
4 Copyright (C) 2006,2007 Benoit Jacob
6 This file is part of the Avogadro molecular editor project.
7 For more information, see <http://avogadro.sourceforge.net/>
9 Avogadro is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 Avogadro is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 **********************************************************************/
27 using namespace Eigen
;
34 SpherePrivate() : vertexBuffer(0), indexBuffer(0), displayList(0), isValid(false) {}
36 /** Pointer to the buffer storing the vertex array */
37 Eigen::Vector3f
*vertexBuffer
;
38 /** Pointer to the buffer storing the indices */
39 unsigned short *indexBuffer
;
40 /** The id of the OpenGL display list */
42 /** the detail-level of the sphere. Must be at least 0.
43 * If 0, the sphere is an octahedron. If >=1, this number is
44 * interpreted as the number of sub-edges into which
45 * each edge of the icosahedron must be split. So the
46 * number of faces of the sphere is simply:
47 * 20 * detail^2. When detail==1, the sphere is just the
54 Sphere::Sphere(int detail
) : d(new SpherePrivate
)
63 glDeleteLists( d
->displayList
, 1 );
67 void Sphere::freeBuffers()
71 delete [] d
->indexBuffer
;
76 delete [] d
->vertexBuffer
;
81 void Sphere::draw(const Eigen::Vector3d
¢er
, double radius
) const
84 glTranslated( center
.x(), center
.y(), center
.z() );
85 glScaled( radius
, radius
, radius
);
86 glCallList( d
->displayList
);
90 void Sphere::initialize()
92 if( d
->detail
< 0 ) return;
94 // deallocate any previously allocated buffer
97 int vertexCount
= 0, indexCount
= 0;
101 if( ! d
->displayList
) { d
->displayList
= glGenLists( 1 ); }
102 if( ! d
->displayList
) { return; }
103 float octahedronVertices
[6][3] = { { 1, 0, 0 } ,
109 #define USE_OCTAHEDRON_VERTEX(i) glNormal3fv(octahedronVertices[i]); \
110 glVertex3fv(octahedronVertices[i]);
111 glNewList( d
->displayList
, GL_COMPILE
);
112 glBegin(GL_TRIANGLE_FAN
);
113 USE_OCTAHEDRON_VERTEX(0);
114 USE_OCTAHEDRON_VERTEX(1);
115 USE_OCTAHEDRON_VERTEX(2);
116 USE_OCTAHEDRON_VERTEX(3);
117 USE_OCTAHEDRON_VERTEX(4);
118 USE_OCTAHEDRON_VERTEX(1);
120 glBegin(GL_TRIANGLE_FAN
);
121 USE_OCTAHEDRON_VERTEX(5);
122 USE_OCTAHEDRON_VERTEX(1);
123 USE_OCTAHEDRON_VERTEX(4);
124 USE_OCTAHEDRON_VERTEX(3);
125 USE_OCTAHEDRON_VERTEX(2);
126 USE_OCTAHEDRON_VERTEX(1);
133 // compute number of vertices and indices
134 vertexCount
= ( 3 * d
->detail
+ 1 ) * ( 5 * d
->detail
+ 1 );
135 indexCount
= (2 * ( 2 * d
->detail
+ 1 ) + 2 ) * 5 * d
->detail
;
137 // allocate memory for buffers
138 d
->vertexBuffer
= new Vector3f
[vertexCount
];
139 if( ! d
->vertexBuffer
) return;
140 d
->indexBuffer
= new unsigned short[indexCount
];
141 if( ! d
->indexBuffer
)
143 delete [] d
->vertexBuffer
;
148 // build vertex buffer
149 for( int strip
= 0; strip
< 5; strip
++ ) {
150 for( int column
= 1; column
< d
->detail
; column
++ ) {
151 for( int row
= column
; row
<= 2 * d
->detail
+ column
; row
++ ) {
152 computeVertex( strip
, column
, row
);
157 for( int strip
= 1; strip
< 5; strip
++ ) {
158 for( int row
= 0; row
<= 3 * d
->detail
; row
++ ) {
159 computeVertex( strip
, 0, row
);
163 for( int row
= 0; row
<= 2 * d
->detail
; row
++ )
164 computeVertex( 0, 0, row
);
166 for( int row
= d
->detail
; row
<= 3 * d
->detail
; row
++ )
167 computeVertex( 4, d
->detail
, row
);
169 // build index buffer
171 for( int strip
= 0; strip
< 5; strip
++ )
172 for( int column
= 0; column
< d
->detail
; column
++ )
175 d
->indexBuffer
[i
++] = indexOfVertex( strip
, column
, row
);
176 for( ; row
<= 2 * d
->detail
+ column
; row
++ )
178 d
->indexBuffer
[i
++] =
179 indexOfVertex( strip
, column
, row
);
180 d
->indexBuffer
[i
++] =
181 indexOfVertex( strip
, column
+ 1, row
+ 1 );
183 d
->indexBuffer
[i
++] = indexOfVertex( strip
, column
+ 1,
184 2 * d
->detail
+ column
+ 1);
187 // compile display list and free buffers
188 if( ! d
->displayList
) { d
->displayList
= glGenLists( 1 ); }
189 if( ! d
->displayList
) { return; }
190 glEnableClientState( GL_VERTEX_ARRAY
);
191 glEnableClientState( GL_NORMAL_ARRAY
);
192 glNewList( d
->displayList
, GL_COMPILE
);
193 glVertexPointer( 3, GL_FLOAT
, 0, d
->vertexBuffer
);
194 glNormalPointer( GL_FLOAT
, 0, d
->vertexBuffer
);
195 glDrawElements( GL_TRIANGLE_STRIP
, indexCount
,
196 GL_UNSIGNED_SHORT
, d
->indexBuffer
);
198 glDisableClientState( GL_VERTEX_ARRAY
);
199 glDisableClientState( GL_NORMAL_ARRAY
);
204 unsigned short Sphere::indexOfVertex( int strip
, int column
, int row
)
206 return ( row
+ ( 3 * d
->detail
+ 1 ) * ( column
+ d
->detail
* strip
) );
209 void Sphere::computeVertex( int strip
, int column
, int row
)
212 int next_strip
= (strip
+ 1) % 5;
214 // the index of the vertex we want to store the result in
215 unsigned short index
= indexOfVertex( strip
, column
, row
);
217 // reference to the vertex we want to store the result in
218 Vector3f
& vertex
= d
->vertexBuffer
[ index
];
220 // the "golden ratio", useful to construct an icosahedron
221 const float phi
= ( 1 + sqrtf(5.0f
) ) / 2;
223 // the 12 vertices of the icosahedron
224 const Vector3f
northPole( 0, 1, phi
);
225 const Vector3f northVertices
[5] = {
226 Vector3f( 0, -1, phi
),
227 Vector3f( phi
, 0, 1 ),
228 Vector3f( 1, phi
, 0 ),
229 Vector3f( -1, phi
, 0 ),
230 Vector3f( -phi
, 0, 1 ) };
231 const Vector3f southVertices
[5] = {
232 Vector3f( -1, -phi
, 0 ),
233 Vector3f( 1, -phi
, 0 ),
234 Vector3f( phi
, 0, -1 ),
235 Vector3f( 0, 1, -phi
),
236 Vector3f( -phi
, 0, -1 )
238 const Vector3f
southPole( 0, -1, -phi
);
240 // pointers to the 3 vertices of the face of the icosahedron
242 const Vector3f
*v0
, *v1
, *v2
;
244 // coordinates of our position inside this face.
245 // range from 0 to d->detail.
248 // first, normalize the global coords row, column
249 if( row
>= 2 * d
->detail
&& column
== 0 )
252 if( strip
< 0 ) strip
+= 5;
254 if( next_strip
< 0 ) next_strip
+= 5;
258 // next, determine in which face we are, and determine the coords
259 // of our position inside this face
260 if( row
<= d
->detail
)
262 v0
= &northVertices
[strip
];
264 v2
= &northVertices
[next_strip
];
265 c1
= d
->detail
- row
;
268 else if( row
>= 2 * d
->detail
)
270 v0
= &southVertices
[next_strip
];
272 v2
= &southVertices
[strip
];
273 c1
= row
- 2 * d
->detail
;
274 c2
= d
->detail
- column
;
276 else if( row
<= d
->detail
+ column
)
278 v0
= &northVertices
[next_strip
];
279 v1
= &southVertices
[next_strip
];
280 v2
= &northVertices
[strip
];
281 c1
= row
- d
->detail
;
282 c2
= d
->detail
- column
;
286 v0
= &southVertices
[strip
];
287 v1
= &southVertices
[next_strip
];
288 v2
= &northVertices
[strip
];
290 c2
= 2 * d
->detail
- row
;
293 // now, compute the actual coords of the vertex
294 float u1
= static_cast<float>(c1
) / d
->detail
;
295 float u2
= static_cast<float>(c2
) / d
->detail
;
296 vertex
= *v0
+ u1
* ( *v1
- *v0
) + u2
* ( *v2
- *v0
);
298 // project the vertex onto the sphere
302 void Sphere::setup( int detail
)
304 if( d
->isValid
&& detail
== d
->detail
) return;