m_vertexBuffer = 0;
m_normalBuffer = 0;
m_indexBuffer = 0;
- m_isInitialized = false;
+ m_displayList = 0;
}
VertexArray::~VertexArray()
if( m_indexBuffer ) delete [] m_indexBuffer;
if( m_vertexBuffer ) delete [] m_vertexBuffer;
if( m_normalBuffer ) delete [] m_normalBuffer;
+ if( m_displayList )
+ glDeleteLists( m_displayList, 1 );
}
bool VertexArray::allocateBuffers()
{
if( m_vertexCount > 65536 ) return false;
- m_isInitialized = false;
-
if( m_indexBuffer )
{
delete [] m_indexBuffer;
if( ! m_normalBuffer ) return false;
m_indexBuffer = new unsigned short[m_indexCount];
if( ! m_indexBuffer ) return false;
+}
- return true;
+void VertexArray::do_draw()
+{
+ glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
+ glEnableClientState( GL_VERTEX_ARRAY );
+ glEnableClientState( GL_NORMAL_ARRAY );
+ glDisableClientState( GL_COLOR_ARRAY );
+ glDisableClientState( GL_EDGE_FLAG_ARRAY );
+ glDisableClientState( GL_INDEX_ARRAY );
+ glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+ glVertexPointer( 3, GL_FLOAT, 0, m_vertexBuffer );
+ glNormalPointer( GL_FLOAT, 0, m_normalBuffer );
+ if( m_indexCount )
+ glDrawElements( m_mode, m_indexCount,
+ GL_UNSIGNED_SHORT, m_indexBuffer );
+ else
+ glDrawArrays( m_mode, 0, m_vertexCount );
+ glPopClientAttrib();
+}
+
+void VertexArray::compileDisplayListIfNeeded()
+{
+#ifdef USE_DISPLAY_LISTS
+ if( ! m_displayList )
+ m_displayList = glGenLists( 1 );
+ if( ! m_displayList ) return;
+ glNewList( m_displayList, GL_COMPILE );
+ do_draw();
+ glEndList();
+
+ delete [] m_vertexBuffer;
+ m_vertexBuffer = 0;
+ delete [] m_normalBuffer;
+ m_normalBuffer = 0;
+ if( m_indexBuffer ) delete [] m_indexBuffer;
+ m_indexBuffer = 0;
+#endif
+}
+
+void VertexArray::initialize()
+{
+ m_vertexCount = computeVertexCount();
+ m_indexCount = computeIndexCount();
+ if( m_indexCount < 0 || m_vertexCount < 0 ) return;
+ allocateBuffers();
+ buildBuffers();
+ compileDisplayListIfNeeded();
}
Sphere::Sphere()
vertex->z *= m_radius;
}
-
-void Sphere::initialize()
+int Sphere::computeVertexCount()
{
- if( m_detail < 1 ) return;
- m_vertexCount = ( 3 * m_detail + 1 ) * ( 5 * m_detail + 1 );
- m_indexCount = (2 * ( 2 * m_detail + 1 ) + 2 ) * 5 * m_detail;
+ if( m_detail < 1 ) return -1;
+ return ( 3 * m_detail + 1 ) * ( 5 * m_detail + 1 );
+}
- if( ! allocateBuffers() ) return;
+int Sphere::computeIndexCount()
+{
+ if( m_detail < 1 ) return -1;
+ return (2 * ( 2 * m_detail + 1 ) + 2 ) * 5 * m_detail;
+}
+void Sphere::buildBuffers()
+{
for( int strip = 0; strip < 5; strip++ )
for( int column = 1; column < m_detail; column++ )
for( int row = column; row <= 2 * m_detail + column; row++ )
m_indexBuffer[i++] = indexOfVertex( strip, column + 1,
2 * m_detail + column + 1);
}
-
- m_isInitialized = true;
}
void Sphere::setup( int detail, GLfloat radius )
initialize();
}
-void Cylinder::initialize()
+int Cylinder::computeVertexCount()
{
- if( m_faces < 3 ) return;
+ if( m_faces < 3 ) return -1;
+ return 2 * m_faces + 2;
+}
- m_vertexCount = 2 * m_faces + 2; // we will use a redundant vertex array
- m_indexCount = 0; // we won't use it.
+int Cylinder::computeIndexCount()
+{
+ if( m_faces < 3 ) return -1;
+ return 0;
+}
- if( ! allocateBuffers() ) return;
- for( int i = 0; i <= m_faces; i++)
+void Cylinder::buildBuffers()
+{
+ for( int i = 0; i <= m_faces; i++ )
{
float angle = 2 * M_PI * i / m_faces;
float x = cosf( angle );
m_normalBuffer[ 2 * i ].y = y;
m_normalBuffer[ 2 * i ].z = 0.0;
- m_vertexBuffer[ 2 * i ].x = x * m_radius ;
+ m_vertexBuffer[ 2 * i ].x = x * m_radius;
m_vertexBuffer[ 2 * i ].y = y * m_radius;
m_vertexBuffer[ 2 * i ].z = 1.0;
- m_normalBuffer[ 2 * i + 1].x = x;
- m_normalBuffer[ 2 * i + 1].y = y;
- m_normalBuffer[ 2 * i + 1].z = 0.0;
+ m_normalBuffer[ 2 * i + 1 ].x = x;
+ m_normalBuffer[ 2 * i + 1 ].y = y;
+ m_normalBuffer[ 2 * i + 1 ].z = 0.0;
- m_vertexBuffer[ 2 * i + 1].x = x * m_radius;
- m_vertexBuffer[ 2 * i + 1].y = y * m_radius ;
- m_vertexBuffer[ 2 * i + 1].z = 0.0;
+ m_vertexBuffer[ 2 * i + 1 ].x = x * m_radius;
+ m_vertexBuffer[ 2 * i + 1 ].y = y * m_radius;
+ m_vertexBuffer[ 2 * i + 1 ].z = 0.0;
}
-
- m_isInitialized = true;
}
CharRenderer::CharRenderer()
painter.begin( &image );
painter.setFont( font );
painter.setRenderHint( QPainter::TextAntialiasing );
- painter.setBackground(Qt::black);
+ painter.setBackground( Qt::black );
painter.eraseRect( image.rect() );
- painter.setPen(Qt::white);
- painter.drawText ( 0, 0, m_width, m_height, Qt::AlignBottom, c);
+ painter.setPen( Qt::blue );
+ painter.drawText ( 0, 0, m_width, m_height, Qt::AlignBottom, c );
painter.end();
- GLubyte *bitmap = new GLubyte [m_width * m_height];
+ GLubyte *bitmap = new GLubyte[ m_width * m_height ];
if( bitmap == 0 ) return false;
- for( int i = 0; i < m_width; i++)
- for( int j = 0; j < m_height; j++)
+ for( int j = m_height - 1, n = 0; j >= 0; j-- )
+ for( int i = 0; i < m_width; i++, n++ )
{
- bitmap[ i + m_width * j ] =
- image.pixel( i, m_height - j - 1 ) & 0x000000ff;
+ bitmap[n] = qBlue( image.pixel( i, j ) );
}
glGenTextures( 1, &m_texture );
if( m_texture == 0 ) return false;
- glBindTexture(GL_TEXTURE_2D,m_texture) ;
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D (
+ glBindTexture( GL_TEXTURE_2D, m_texture );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+ glTexImage2D(
GL_TEXTURE_2D,
0,
GL_ALPHA,
glNewList( m_displayList, GL_COMPILE );
glBindTexture( GL_TEXTURE_2D, m_texture );
- glBegin(GL_QUADS);
+ glBegin( GL_QUADS );
glTexCoord2f( 0, 0);
glVertex2f( 0 , 0 );
glTexCoord2f( 1, 0);
glEnd();
glTranslatef( m_width, 0, 0 );
glEndList();
-
return true;
}
m_isBetweenBeginAndEnd = false;
}
-void TextRenderer::print( int x, int y, const QString &string)
+void TextRenderer::print( int x, int y, const QString &string )
{
if( ! m_glwidget ) return;
if( string.isEmpty() ) return;
glPushMatrix();
glLoadIdentity();
glTranslatef( x, y, 0 );
- for(int i = 0; i < string.size(); i++)
+ for( int i = 0; i < string.size(); i++ )
{
if( m_charTable.contains( string[i] ) )
- m_charTable.value(string[i])->draw();
+ m_charTable.value( string[i] )->draw();
else
{
CharRenderer *c = new CharRenderer;
*/
#define USE_DOUBLE_PRECISION
-/** USE_FPS_COUNTER: if defined, the GL Widgets will show a frames-per-second
- * counter. *Use only for testing*: this makes the GL Widget constantly
- * redraw, which under normal circumstances is a waste of CPU power.
+/** USE_FPS_COUNTER: if defined, the GL Widget will show a frames-per-second
+ * counter. Use only for testing: this makes the GL Widget constantly
+ * redraw, which under normal circumstances is a waste of CPU time.
*/
#define USE_FPS_COUNTER
+/** USE_DISPLAY_LISTS: if defined, the whole scene will be stored in
+ * an OpenGL display list. The vertex arrays will then also be converted to
+ * display lists, in order to avoid problems. This option improves performance,
+ * especially when rendering complex models, but increases memory usage.
+ */
+#define USE_DISPLAY_LISTS
+
namespace KalziumGLHelpers
{
* returns true if abs( a - b ) <= c * precision
* where c = max( abs( a ), abs( b ) )
*/
-template<class T> static bool approx_equal( T a, T b, T precision )
+template<class T> bool approx_equal( T a, T b, T precision )
{
T abs_a = FABS( a );
T abs_b = FABS( b );
*/
class VertexArray
{
-
protected:
GLenum m_mode;
Vector3<GLfloat> *m_vertexBuffer;
Vector3<GLfloat> *m_normalBuffer;
- unsigned int m_vertexCount;
+ int m_vertexCount;
unsigned short *m_indexBuffer;
- unsigned int m_indexCount;
-
- bool m_isInitialized;
-
- virtual void initialize() = 0;
+ int m_indexCount;
+ GLuint m_displayList;
+
+ virtual int computeVertexCount() = 0;
+ virtual int computeIndexCount() = 0;
+ virtual void buildBuffers() = 0;
virtual bool allocateBuffers();
+ virtual void compileDisplayListIfNeeded();
+
+ virtual void initialize();
public:
VertexArray();
virtual ~VertexArray();
- virtual inline void select()
- {
- glVertexPointer( 3, GL_FLOAT, 0, m_vertexBuffer );
- glNormalPointer( GL_FLOAT, 0, m_normalBuffer );
- }
+
+ virtual void do_draw();
virtual inline void draw()
{
- select();
- glDrawElements( m_mode, m_indexCount,
- GL_UNSIGNED_SHORT, m_indexBuffer );
+#ifdef USE_DISPLAY_LISTS
+ glCallList( m_displayList );
+#else
+ do_draw();
+#endif
}
};
protected:
int m_detail;
GLfloat m_radius;
-
- virtual void initialize();
+ virtual int computeVertexCount();
+ virtual int computeIndexCount();
+ virtual void buildBuffers();
public:
Sphere();
int m_faces;
GLfloat m_radius;
- virtual void initialize();
+ virtual int computeVertexCount();
+ virtual int computeIndexCount();
+ virtual void buildBuffers();
public:
Cylinder();
virtual ~Cylinder() {}
virtual void setup( int detail, GLfloat radius );
- virtual inline void draw()
- {
- select();
- glDrawArrays( m_mode, 0, m_vertexCount );
- }
};
/** This is a helper class for TextRenderer, and should probably never be
};
* @endcode
*
-* Now, in the constructor of MyGLWidget, you please call setup()
-* along these lines:
+* Now, in the constructor of MyGLWidget, please call setup() along these lines:
*
* @code
QFont f;
m_textRenderer.end();
* @endcode
*
-* Please make sure, though, that no OpenGL state change occurs between begin()
-* and end(), except the state changes performed by the TextRenderer itself.
-* In other words, please avoid calling glSomething() between begin() and end(),
-* except if you are sure that this call won't perform a relevant state change.
+* Please make sure, though, that no relevant OpenGL state change occurs between
+* begin() and end(), except the state changes performed by the TextRenderer
+* itself. In other words, please avoid calling glSomething() between begin() and
+* end(), except if you are sure that this call won't perform a relevant state
+* change.
*
* The print() method when called alone, or the begin()-print()-end() group,
* do restore the OpenGL state as they found it, including the matrices.
* This hash gives the correspondence table between QChars
* (the keys) and the corresponding CharRenderers (the values).
* Every time a QChar is being met, either it is found in this
- * table, in which case it can be directly rendered, or it is not
- * found, in which case a new CharRenderer is created for it,
- * and added to this table.
+ * table, in which case it can be directly rendered, or it is
+ * not found, in which case a new CharRenderer is created for
+ * it and added to this table.
*/
QHash<QChar, CharRenderer*> m_charTable;
m_isDragging = false;
m_molecule = 0;
m_detail = 0;
+ m_displayList = 0;
+ m_haveToRecompileDisplayList = true;
m_useFog = false;
m_inZoom = false;
m_inMeasure = false;
glGetDoublev( GL_MODELVIEW_MATRIX, m_RotationMatrix );
glPopMatrix();
- glEnable( GL_RESCALE_NORMAL_EXT );
+ //glEnable( GL_RESCALE_NORMAL_EXT );
glEnable(GL_LIGHT0);
GLfloat specularLight[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat position[] = { 0.8, 0.7, 1.0, 0.0 };
- glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
- glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
- glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
- glLightfv(GL_LIGHT0, GL_POSITION, position);
+ glLightfv( GL_LIGHT0, GL_AMBIENT, ambientLight );
+ glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseLight );
+ glLightfv( GL_LIGHT0, GL_SPECULAR, specularLight );
+ glLightfv( GL_LIGHT0, GL_POSITION, position );
GLfloat fogColor[] = { 0.0, 0.0, 0.0, 1.0 };
glFogfv( GL_FOG_COLOR, fogColor );
glEnable( GL_COLOR_SUM_EXT );
glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL_EXT,
GL_SEPARATE_SPECULAR_COLOR_EXT );
-
- glEnableClientState( GL_VERTEX_ARRAY );
- glEnableClientState( GL_NORMAL_ARRAY );
-
- setupObjects();
}
void KalziumGLWidget::paintGL()
if( ! m_molecule )
{
glColor3f( 0.0, 1.0, 0.6 );
- glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+ glClear( GL_COLOR_BUFFER_BIT );
m_textRenderer.print( 20, height() - 40, i18n("Please load a molecule") );
return;
}
}
else glDisable( GL_FOG );
+#ifdef USE_DISPLAY_LISTS
+ if( m_haveToRecompileDisplayList )
+ {
+ if( ! m_displayList ) m_displayList = glGenLists( 1 );
+ if( ! m_displayList ) return;
+ glNewList( m_displayList, GL_COMPILE );
+#endif
+
// prepare for rendering the spheres
if( m_atomStyle == ATOM_SPHERE )
{
}
}
}
+#ifdef USE_DISPLAY_LISTS
+ glEndList();
+ m_haveToRecompileDisplayList = false;
+ }
+ glCallList( m_displayList );
+#endif
// now, paint a semitransparent sphere around the selected atoms
if( m_selectedAtoms.count() > 0 )//there are items selected
glDisable( GL_BLEND );
}
}
+
#ifdef USE_FPS_COUNTER
QTime t;
s = QString::number( 1000 * frames /
double( new_time - old_time ),
'f', 1 );
- s += " frames per second" ;
+ s += " FPS";
frames = 0;
old_time = new_time;
}
void KalziumGLWidget::rotate( )
{
+// OK, let's momentarily disable that until I get it working (Benoit)
+
+/*
kDebug() << "KalziumGLWidget::rotate()" << endl;
//TODO at this place we need a nice way to rotate
//based on certain values. For example, we could use two
glGetDoublev( GL_MODELVIEW_MATRIX, m_RotationMatrix );
glPopMatrix();
updateGL();
+*/
}
void KalziumGLWidget::setupObjects()
{
if ( !molecule ) return;
m_molecule = molecule;
+ m_haveToRecompileDisplayList = true;
prepareMoleculeData();
setupObjects();
updateGL();
m_bondRadiusCoeff = 0.0;
break;
}
+}
+
+
+void KalziumGLWidget::slotChooseStylePreset( int stylePreset )
+{
+ ChooseStylePreset( (StylePreset) stylePreset );
+ m_haveToRecompileDisplayList = true;
setupObjects();
updateGL();
}
void KalziumGLWidget::prepareMoleculeData()
{
- // translate the molecule so that center has coords 0,0,0
- m_molecule->Center();
+ //Center the molecule
+ //normally this is done by OBMol::Center()
+ //but it doesn't seem to work for me
+ //perhaps I'm stupid (Benoit 03/07/06)
+
+ //first, calculate the coords of the center of the molecule
+ vector3 center( 0.0, 0.0, 0.0);
+ int number_of_atoms = 0;
+ FOR_ATOMS_OF_MOL( a, m_molecule )
+ {
+ center += a->GetVector();
+ number_of_atoms++;
+ }
+ center /= number_of_atoms;
+
+ //now, translate the molecule so that it gets centered.
+ //unfortunately OBMol::Translate doesn't seem to work for me
+ //(Benoit 03/07/06)
+ FOR_ATOMS_OF_MOL( a, m_molecule )
+ {
+ vector3 new_vector = a->GetVector() - center;
+ a->SetVector( new_vector );
+ }
// calculate the radius of the molecule
// that is, the maximal distance between an atom of the molecule
Color& KalziumGLWidget::getAtomColor( OpenBabel::OBAtom* atom )
{
+ // thanks to Geoffrey Hutchison from OpenBabel for
+ // this simplified getAtomColor method
static Color c;
-
- if ( atom->IsHydrogen() )
- {//white
- c.m_red = 1.0;
- c.m_green = 1.0;
- c.m_blue = 1.0;
- }
- else if ( atom->IsCarbon() )
- {//almost black
- c.m_red = 0.25;
- c.m_green = 0.25;
- c.m_blue = 0.25;
- }
- else if ( atom->IsOxygen() )
- {//red
- c.m_red = 1.0;
- c.m_green = 0.0;
- c.m_blue = 0.0;
- }
- else if ( atom->IsNitrogen() )
- {
- c.m_red = 1.0;
- c.m_green = 0.9;
- c.m_blue = 0.5;
- }
- else if ( atom->IsSulfur() )
- {//yellow
- c.m_red = 1.0;
- c.m_green = 1.0;
- c.m_blue = 0.0;
- }
- else
- {
- c.m_red = 0.5;
- c.m_green = 0.5;
- c.m_blue = 0.5;
- }
-
+ c.m_red = etab.GetRGB(atom->GetAtomicNum())[0];
+ c.m_green = etab.GetRGB(atom->GetAtomicNum())[1];
+ c.m_blue = etab.GetRGB(atom->GetAtomicNum())[2];
return c;
}
Q_OBJECT
protected:
+ GLuint m_displayList;
+ bool m_haveToRecompileDisplayList;
+
TextRenderer m_textRenderer;
/**
* Chooses the style of rendering among some presets
* @param stylePreset the wanted style preset
*/
- void slotChooseStylePreset( int stylePreset ){
- ChooseStylePreset( (StylePreset) stylePreset );
- }
+ void slotChooseStylePreset( int stylePreset );
/**
* The atoms @p atoms was selected by the user