From: BenoĆ®t Jacob Date: Thu, 20 Jul 2006 12:39:14 +0000 (+0000) Subject: Prerequisite: you'll need OpenBabel SVN revision at least 1525. X-Git-Tag: v3.80.3~103^2~27 X-Git-Url: https://git.rmz.fi/?a=commitdiff_plain;h=f44bc0f17f4a7de2e0555dea7a9e7448a282933a;p=libqmvoc.git Prerequisite: you'll need OpenBabel SVN revision at least 1525. Reorganization everywhere, especially: - Make much more use of OpenBabel, especially for the geometry-stuff - Molecule rendering style and related data is now stored using the MolStyle struct. - Big reorganization in the way display lists are used, when this option is enabled. It is currently disabled, though, because of what might be a bug in Mesa3d. New Features: - draw multiple bonds - support for van der Waals radius. Different atoms are now rendered with different sizes. Optimizations: - the sphere now uses 2x less memory when not using display lists (achieved by using the same unique array as vertex&normal arrays) Other changes: - improved specular color computation in Color::applyAsMaterials() - reorganized styles, removed "lines" style (looked useless, was inconsistent with multiple bonds) - when the uses clicks "cancel" in the Load molecule dialog, don't try to load the molecule with filename "". - lots more that I can't list CCMAIL: geoff@geoffhutchison.net M src/kalziumglwidget.h M src/kalziumglhelperclasses.h M src/moleculeview.cpp M src/kalziumglwidget.cpp M src/kalziumglhelperclasses.cpp M src/moleculeviewerwidget.ui svn path=/trunk/KDE/kdeedu/kalzium/src/kalziumglwidget.h; revision=564535 --- diff --git a/kalzium/kalziumglhelperclasses.cpp b/kalzium/kalziumglhelperclasses.cpp index ebf8261..bb249bd 100644 --- a/kalzium/kalziumglhelperclasses.cpp +++ b/kalzium/kalziumglhelperclasses.cpp @@ -13,14 +13,34 @@ #include "kalziumglhelperclasses.h" #include -#ifdef USE_FPS_COUNTER -#include -#endif - using namespace KalziumGLHelpers; +using namespace OpenBabel; + +void MolStyle::setup( BondStyle bondStyle, AtomStyle atomStyle, + double singleBondRadius, + double multipleBondRadius, + double multipleBondShift, + double atomRadiusFactor ) +{ + m_bondStyle = bondStyle; + m_atomStyle = atomStyle; + m_singleBondRadius = singleBondRadius; + m_multipleBondRadius = multipleBondRadius; + m_multipleBondShift = multipleBondShift; + m_atomRadiusFactor = atomRadiusFactor; +} -Color::Color() +double MolStyle::getAtomRadius( int atomicNumber ) { + switch( m_atomStyle ) + { + case ATOMS_USE_FIXED_RADIUS: + return m_atomRadiusFactor; + case ATOMS_USE_VAN_DER_WAALS_RADIUS: + return m_atomRadiusFactor + * etab.GetVdwRad( atomicNumber ); + default: return 0; + } } Color::Color( GLfloat red, GLfloat green, GLfloat blue, @@ -32,6 +52,15 @@ Color::Color( GLfloat red, GLfloat green, GLfloat blue, m_alpha = alpha; } +Color::Color( const OBAtom* atom ) +{ + std::vector rgb = etab.GetRGB( atom->GetAtomicNum() ); + m_red = rgb[0]; + m_green = rgb[1]; + m_blue = rgb[2]; + m_alpha = 1.0; +} + Color& Color::operator=( const Color& other ) { m_red = other.m_red; @@ -47,8 +76,16 @@ void Color::applyAsMaterials() GLfloat ambientColor [] = { m_red / 2, m_green / 2, m_blue / 2, m_alpha }; GLfloat diffuseColor [] = { m_red, m_green, m_blue, m_alpha }; - GLfloat specularColor [] = { (2.0 + m_red) / 3, (2.0 + m_green) / 3, - (2.0 + m_blue) / 3, m_alpha }; + + float s = ( 1.0 + fabsf( m_red - m_green ) + + fabsf( m_blue - m_green ) + fabsf( m_blue - m_red ) ) / 3; + + float t = 1.0 - s; + + GLfloat specularColor [] = { s + t * m_red, + s + t * m_green, + s + t * m_blue, + m_alpha }; glMaterialfv(GL_FRONT, GL_AMBIENT, ambientColor); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseColor); @@ -56,28 +93,29 @@ void Color::applyAsMaterials() glMaterialf(GL_FRONT, GL_SHININESS, 50.0); } -VertexArray::VertexArray() +VertexArray::VertexArray( GLenum mode, + bool hasIndexBuffer, + bool hasSeparateNormalBuffer ) { - m_mode = GL_TRIANGLE_STRIP; + m_mode = mode; m_vertexBuffer = 0; m_normalBuffer = 0; m_indexBuffer = 0; m_displayList = 0; + m_hasIndexBuffer = hasIndexBuffer; + m_hasSeparateNormalBuffer = hasSeparateNormalBuffer; + m_isValid = false; } VertexArray::~VertexArray() { - if( m_indexBuffer ) delete [] m_indexBuffer; - if( m_vertexBuffer ) delete [] m_vertexBuffer; - if( m_normalBuffer ) delete [] m_normalBuffer; + freeBuffers(); if( m_displayList ) glDeleteLists( m_displayList, 1 ); } -bool VertexArray::allocateBuffers() +void VertexArray::freeBuffers() { - if( m_vertexCount > 65536 ) return false; - if( m_indexBuffer ) { delete [] m_indexBuffer; @@ -88,73 +126,80 @@ bool VertexArray::allocateBuffers() delete [] m_vertexBuffer; m_vertexBuffer = 0; } - if( m_normalBuffer ) + if( m_normalBuffer && m_hasSeparateNormalBuffer ) { delete [] m_normalBuffer; m_normalBuffer = 0; } +} + +bool VertexArray::allocateBuffers() +{ + if( m_vertexCount > 65536 ) return false; - m_vertexBuffer = new Vector3[m_vertexCount]; + freeBuffers(); + + m_vertexBuffer = new Vector[m_vertexCount]; if( ! m_vertexBuffer ) return false; - m_normalBuffer = new Vector3[m_vertexCount]; - if( ! m_normalBuffer ) return false; - m_indexBuffer = new unsigned short[m_indexCount]; - if( ! m_indexBuffer ) return false; + + if( m_hasSeparateNormalBuffer ) + { + m_normalBuffer = new Vector[m_vertexCount]; + if( ! m_normalBuffer ) return false; + } + else m_normalBuffer = m_vertexBuffer; + + if( m_hasIndexBuffer ) + { + 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 ) + if( m_hasIndexBuffer ) glDrawElements( m_mode, m_indexCount, GL_UNSIGNED_SHORT, m_indexBuffer ); else glDrawArrays( m_mode, 0, m_vertexCount ); - glPopClientAttrib(); } -void VertexArray::compileDisplayListIfNeeded() +void VertexArray::compileDisplayList() { #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; + freeBuffers(); #endif } void VertexArray::initialize() { + m_isValid = false; m_vertexCount = computeVertexCount(); m_indexCount = computeIndexCount(); if( m_indexCount < 0 || m_vertexCount < 0 ) return; - allocateBuffers(); + if( ! allocateBuffers() ) return; buildBuffers(); - compileDisplayListIfNeeded(); + compileDisplayList(); + m_isValid = true; } Sphere::Sphere() - : VertexArray() + : VertexArray( GL_TRIANGLE_STRIP, true, false ) { m_detail = 0; - m_radius = -1.0; } unsigned short Sphere::indexOfVertex( int strip, int column, int row) @@ -167,31 +212,27 @@ void Sphere::computeVertex( int strip, int column, int row) strip %= 5; int next_strip = (strip + 1) % 5; - Vector3 *vertex = - &m_vertexBuffer[ indexOfVertex( strip, column, row ) ]; - - Vector3 *normal = - &m_normalBuffer[ indexOfVertex( strip, column, row ) ]; - - const GLfloat phi = ( 1 + sqrt(5) ) / 2; - - const Vector3 northPole( 0, 1, phi ); - const Vector3 northVertex[5] = { - Vector3( 0, -1, phi ), - Vector3( phi, 0, 1 ), - Vector3( 1, phi, 0 ), - Vector3( -1, phi, 0 ), - Vector3( -phi, 0, 1 ) }; - const Vector3 southVertex[5] = { - Vector3( -1, -phi, 0 ), - Vector3( 1, -phi, 0 ), - Vector3( phi, 0, -1 ), - Vector3( 0, 1, -phi ), - Vector3( -phi, 0, -1 ) + unsigned short index = indexOfVertex( strip, column, row ); + + const double phi = ( 1 + sqrt(5) ) / 2; + + const vector3 northPole( 0, 1, phi ); + const vector3 northVertices[5] = { + vector3( 0, -1, phi ), + vector3( phi, 0, 1 ), + vector3( 1, phi, 0 ), + vector3( -1, phi, 0 ), + vector3( -phi, 0, 1 ) }; + const vector3 southVertices[5] = { + vector3( -1, -phi, 0 ), + vector3( 1, -phi, 0 ), + vector3( phi, 0, -1 ), + vector3( 0, 1, -phi ), + vector3( -phi, 0, -1 ) }; - const Vector3 southPole( 0, -1, -phi ); + const vector3 southPole( 0, -1, -phi ); - const Vector3 *v0, *v1, *v2; + const vector3 *v0, *v1, *v2; int c1, c2; if( row >= 2 * m_detail && column == 0 ) @@ -205,51 +246,51 @@ void Sphere::computeVertex( int strip, int column, int row) if( row <= m_detail ) { - v0 = &northVertex[strip]; + v0 = &northVertices[strip]; v1 = &northPole; - v2 = &northVertex[next_strip]; + v2 = &northVertices[next_strip]; c1 = m_detail - row; c2 = column; } else if( row >= 2 * m_detail ) { - v0 = &southVertex[next_strip]; + v0 = &southVertices[next_strip]; v1 = &southPole; - v2 = &southVertex[strip]; + v2 = &southVertices[strip]; c1 = row - 2 * m_detail; c2 = m_detail - column; } else if( row <= m_detail + column ) { - v0 = &northVertex[next_strip]; - v1 = &southVertex[next_strip]; - v2 = &northVertex[strip]; + v0 = &northVertices[next_strip]; + v1 = &southVertices[next_strip]; + v2 = &northVertices[strip]; c1 = row - m_detail; c2 = m_detail - column; } else { - v0 = &southVertex[strip]; - v1 = &southVertex[next_strip]; - v2 = &northVertex[strip]; + v0 = &southVertices[strip]; + v1 = &southVertices[next_strip]; + v2 = &northVertices[strip]; c1 = column; c2 = 2 * m_detail - row; } - GLfloat u1 = GLfloat(c1) / m_detail; - GLfloat u2 = GLfloat(c2) / m_detail; + double u1 = double(c1) / m_detail; + double u2 = double(c2) / m_detail; - vertex->x = v0->x + u1 * (v1->x - v0->x) + u2 * (v2->x - v0->x); - vertex->y = v0->y + u1 * (v1->y - v0->y) + u2 * (v2->y - v0->y); - vertex->z = v0->z + u1 * (v1->z - v0->z) + u2 * (v2->z - v0->z); + vector3 v; + v.SetX( v0->x() + u1 * (v1->x() - v0->x()) + u2 * (v2->x() - v0->x()) ); + v.SetY( v0->y() + u1 * (v1->y() - v0->y()) + u2 * (v2->y() - v0->y()) ); + v.SetZ( v0->z() + u1 * (v1->z() - v0->z()) + u2 * (v2->z() - v0->z()) ); + v.normalize(); - vertex->normalize(); - - *normal = *vertex; - - vertex->x *= m_radius; - vertex->y *= m_radius; - vertex->z *= m_radius; + Vector *vertex = + &m_vertexBuffer[ index ]; + vertex->x = v.x(); + vertex->y = v.y(); + vertex->z = v.z(); } int Sphere::computeVertexCount() @@ -299,46 +340,32 @@ void Sphere::buildBuffers() } } -void Sphere::setup( int detail, GLfloat radius ) +void Sphere::setup( int detail ) { - if( detail == m_detail && radius == m_radius ) return; + if( detail == m_detail ) return; m_detail = detail; - m_radius = radius; initialize(); } -void Sphere::drawScaled( GLfloat radius ) +void Sphere::draw( const vector3 ¢er, double radius ) { - const GLfloat precision = 0.001; - - if( approx_equal( radius, m_radius, precision ) ) - { - draw(); - return; - } - - GLfloat factor = radius / m_radius; -// glEnable( GL_NORMALIZE ); glPushMatrix(); - glScalef( factor, factor, factor ); - draw(); + glTranslated( center.x(), center.y(), center.z() ); + glScaled( radius, radius, radius ); + VertexArray::draw(); glPopMatrix(); -// glDisable( GL_NORMALIZE ); } Cylinder::Cylinder() - : VertexArray() + : VertexArray( GL_QUAD_STRIP, false, true ) { - m_mode = GL_QUAD_STRIP; m_faces = 0; - m_radius = -1.0; } -void Cylinder::setup( int faces, GLfloat radius ) +void Cylinder::setup( int faces ) { - if( faces == m_faces && radius == m_radius ) return; + if( faces == m_faces ) return; m_faces = faces; - m_radius = radius; initialize(); } @@ -348,13 +375,6 @@ int Cylinder::computeVertexCount() return 2 * m_faces + 2; } -int Cylinder::computeIndexCount() -{ - if( m_faces < 3 ) return -1; - return 0; -} - - void Cylinder::buildBuffers() { for( int i = 0; i <= m_faces; i++ ) @@ -367,20 +387,74 @@ void Cylinder::buildBuffers() 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 ].y = y * m_radius; + m_vertexBuffer[ 2 * i ].x = x; + m_vertexBuffer[ 2 * i ].y = y; 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_vertexBuffer[ 2 * i + 1 ].x = x * m_radius; - m_vertexBuffer[ 2 * i + 1 ].y = y * m_radius; + m_vertexBuffer[ 2 * i + 1 ].x = x; + m_vertexBuffer[ 2 * i + 1 ].y = y; m_vertexBuffer[ 2 * i + 1 ].z = 0.0; } } +void Cylinder::draw( const vector3 &end1, const vector3 &end2, + double radius, int order, double shift ) +{ + // the "axis vector" of the cylinder + vector3 axis = end2 - end1; + + // find two unit vectors v, w such that + // (axis,v,w) is an orthogonal basis + vector3 v, w; + createOrthoBasisGivenFirstVector( axis, v, w ); + + // construct the 4D transformation matrix + GLdouble matrix[16]; + + // column 1 + matrix[0] = v.x() * radius; + matrix[1] = v.y() * radius; + matrix[2] = v.z() * radius; + matrix[3] = 0.0; + + // column 2 + matrix[4] = w.x() * radius; + matrix[5] = w.y() * radius; + matrix[6] = w.z() * radius; + matrix[7] = 0.0; + + // column 3 + matrix[8] = axis.x(); + matrix[9] = axis.y(); + matrix[10] = axis.z(); + matrix[11] = 0.0; + + // column 4 + matrix[12] = end1.x(); + matrix[13] = end1.y(); + matrix[14] = end1.z(); + matrix[15] = 1.0; + + //now we can do the actual drawing ! + glPushMatrix(); + glMultMatrixd( matrix ); + + if( order == 1 ) VertexArray::draw(); + else for( int i = 0; i < order; i++) + { + glPushMatrix(); + glRotated( 360.0 * i / order, 0.0, 0.0, 1.0 ); + glTranslated( shift / radius, 0.0, 0.0 ); + VertexArray::draw(); + glPopMatrix(); + } + glPopMatrix(); +} + CharRenderer::CharRenderer() { m_texture = 0; @@ -392,14 +466,15 @@ CharRenderer::~CharRenderer() if( m_texture ) glDeleteTextures( 1, &m_texture ); if( m_displayList ) glDeleteLists( m_displayList, 1 ); } -bool CharRenderer::initialize( QChar c, const QFont &font ) + +bool CharRenderer::initialize( QChar c, const QFont &font, GLenum textureTarget ) { if( m_displayList ) return true; QFontMetrics fontMetrics ( font ); m_width = fontMetrics.width( c ); m_height = fontMetrics.height(); - if( m_width == 0 || m_height == 0 ) return false; + if( m_width <= 0 || m_height <= 0 ) return false; QImage image( m_width, m_height, QImage::Format_RGB32 ); QPainter painter; @@ -413,7 +488,7 @@ bool CharRenderer::initialize( QChar c, const QFont &font ) painter.end(); GLubyte *bitmap = new GLubyte[ m_width * m_height ]; - if( bitmap == 0 ) return false; + if( ! bitmap ) return false; for( int j = m_height - 1, n = 0; j >= 0; j-- ) for( int i = 0; i < m_width; i++, n++ ) @@ -422,12 +497,12 @@ bool CharRenderer::initialize( QChar c, const QFont &font ) } glGenTextures( 1, &m_texture ); - if( m_texture == 0 ) return false; + if( ! m_texture ) return false; - glBindTexture( GL_TEXTURE_2D, m_texture ); + glBindTexture( textureTarget, m_texture ); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glTexImage2D( - GL_TEXTURE_2D, + textureTarget, 0, GL_ALPHA, m_width, @@ -436,16 +511,16 @@ bool CharRenderer::initialize( QChar c, const QFont &font ) GL_ALPHA, GL_UNSIGNED_BYTE, bitmap ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri( textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); delete [] bitmap; m_displayList = glGenLists(1); - if( m_displayList == 0 ) return false; + if( ! m_displayList ) return false; glNewList( m_displayList, GL_COMPILE ); - glBindTexture( GL_TEXTURE_2D, m_texture ); + glBindTexture( textureTarget, m_texture ); glBegin( GL_QUADS ); glTexCoord2f( 0, 0); glVertex2f( 0 , 0 ); @@ -465,6 +540,7 @@ TextRenderer::TextRenderer() { m_glwidget = 0; m_isBetweenBeginAndEnd = false; + m_textureTarget = GL_TEXTURE_2D; } TextRenderer::~TextRenderer() @@ -488,12 +564,13 @@ void TextRenderer::do_begin() { m_wasEnabled_LIGHTING = glIsEnabled( GL_LIGHTING ); m_wasEnabled_FOG = glIsEnabled( GL_FOG ); - m_wasEnabled_TEXTURE_2D = glIsEnabled( GL_TEXTURE_2D ); + m_wasEnabled_textureTarget + = glIsEnabled( m_textureTarget ); m_wasEnabled_BLEND = glIsEnabled( GL_BLEND ); m_wasEnabled_DEPTH_TEST = glIsEnabled( GL_DEPTH_TEST ); glDisable( GL_LIGHTING ); glDisable( GL_FOG ); - glEnable( GL_TEXTURE_2D ); + glEnable( m_textureTarget ); glEnable( GL_BLEND ); glDisable( GL_DEPTH_TEST ); glMatrixMode( GL_PROJECTION ); @@ -513,7 +590,8 @@ void TextRenderer::begin() void TextRenderer::do_end() { - if( ! m_wasEnabled_TEXTURE_2D ) glDisable( GL_TEXTURE_2D); + if( ! m_wasEnabled_textureTarget ) + glDisable( m_textureTarget ); if( ! m_wasEnabled_BLEND ) glDisable( GL_BLEND ); if( m_wasEnabled_DEPTH_TEST ) glEnable( GL_DEPTH_TEST ); if( m_wasEnabled_LIGHTING ) glEnable( GL_LIGHTING ); @@ -546,7 +624,7 @@ void TextRenderer::print( int x, int y, const QString &string ) else { CharRenderer *c = new CharRenderer; - if( c->initialize( string[i], m_font ) ) + if( c->initialize( string[i], m_font, m_textureTarget ) ) { m_charTable.insert( string[i], c); c->draw(); @@ -558,3 +636,13 @@ void TextRenderer::print( int x, int y, const QString &string ) if( ! m_isBetweenBeginAndEnd ) do_end(); } + +bool KalziumGLHelpers::createOrthoBasisGivenFirstVector + ( const vector3 &U, vector3 & v, vector3 & w ) +{ + if( ! U.createOrthoVector( v ) ) return false; + w = cross( U, v ); + w.normalize(); + return true; +} + diff --git a/kalzium/kalziumglhelperclasses.h b/kalzium/kalziumglhelperclasses.h index 45351d7..224f9b7 100644 --- a/kalzium/kalziumglhelperclasses.h +++ b/kalzium/kalziumglhelperclasses.h @@ -5,12 +5,12 @@ ***************************************************************************/ /*************************************************************************** - * * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * + * (at your option) any later version. * + * * ***************************************************************************/ #include #include @@ -20,12 +20,8 @@ #include #include -/** USE_DOUBLE_PRECISION: if defined, use doubles instead of floats for - * handling the model's geometric data. This does not seem to impact - * significantly the performance. The vertex arrays are unaffected: they - * always use floats. - */ -#define USE_DOUBLE_PRECISION +#include +#include /** 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 @@ -34,40 +30,55 @@ #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. + * an OpenGL display list. The vertex arrays will then be converted into + * (nested) display lists, in order to avoid replication of geometric data. + * This option improves performance, especially when rendering complex models, + * but increases memory usage. */ -#define USE_DISPLAY_LISTS +//#define USE_DISPLAY_LISTS namespace KalziumGLHelpers { -#ifdef USE_DOUBLE_PRECISION -typedef double FLOAT; -typedef GLdouble GLFLOAT; -#else -typedef float FLOAT; -typedef GLfloat GLFLOAT; -#endif +/** + * This struct represents a style in which to render a molecule + * + * @author Benoit Jacob + */ +struct MolStyle +{ + enum BondStyle + { + BONDS_DISABLED, + BONDS_GRAY, + BONDS_USE_ATOMS_COLORS + } m_bondStyle; -inline float SQRT( float x ) { return sqrtf( x ); } -inline double SQRT( double x ) { return sqrt( x ); } -inline float SIN( float x ) { return sinf( x ); } -inline double SIN( double x ) { return sin( x ); } -inline float COS( float x ) { return cosf( x ); } -inline double COS( double x ) { return cos( x ); } -inline float FABS( float x ) { return fabsf( x ); } -inline double FABS( double x ) { return fabs( x ); } -inline void GLMULTMATRIX( const GLfloat *m ) { glMultMatrixf(m); } -inline void GLMULTMATRIX( const GLdouble *m ) { glMultMatrixd(m); } -inline void GLTRANSLATE( GLfloat x, GLfloat y, GLfloat z ) \ - { glTranslatef( x, y, z ); } -inline void GLTRANSLATE( GLdouble x, GLdouble y, GLdouble z ) \ - { glTranslated( x, y, z ); } + enum AtomStyle + { + ATOMS_DISABLED, + ATOMS_USE_FIXED_RADIUS, + ATOMS_USE_VAN_DER_WAALS_RADIUS, + } m_atomStyle; + + double m_singleBondRadius; + double m_multipleBondRadius; + double m_multipleBondShift; + double m_atomRadiusFactor; + + void setup( BondStyle bondStyle, AtomStyle atomStyle, + double singleBondRadius, + double multipleBondRadius, + double multipleBondShift, + double atomRadiusFactor ); + + double getAtomRadius( int atomicNumber ); + inline double getAtomRadius( const OpenBabel::OBAtom *atom ) + { return getAtomRadius( atom->GetAtomicNum() ); } +}; /** -* This class represents a color in OpenGL float red-green-blue format. +* This class represents a color in OpenGL float red-green-blue-alpha format. * * @author Benoit Jacob */ @@ -75,10 +86,23 @@ struct Color { GLfloat m_red, m_green, m_blue, m_alpha; - Color(); + Color() {} + + /** + * This constructor sets the four components of the color + * individually. Each one ranges from 0.0 (lowest intensity) to + * 1.0 (highest intensity). For the alpha component, 0.0 means fully + * transparent and 1.0 (the default) means fully opaque. + */ Color( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha = 1.0 ); + /** + * This constructor uses OpenBabel to retrieve the color in which + * the atom should be rendered. + */ + Color( const OpenBabel::OBAtom *atom ); + Color& operator=( const Color& other ); /** @@ -99,115 +123,13 @@ struct Color }; /** -* Tests whether two Ts are approximately equal. Here T is assumed to be -* a floating-point type. Recall that operator== between floating-point -* types is broken. -* returns true if abs( a - b ) <= c * precision -* where c = max( abs( a ), abs( b ) ) -*/ -template bool approx_equal( T a, T b, T precision ) -{ - T abs_a = FABS( a ); - T abs_b = FABS( b ); - - T max_abs; - if( abs_a <= abs_b ) - max_abs = abs_b; - else - max_abs = abs_a; - return( FABS( a - b ) <= precision * max_abs ); -} - -/** -* This template class represents a vector in 3-space. It is meant to be -* used with T = a floating-point type. -* -* @author Benoit Jacob -*/ -template class Vector3 -{ - public: - T x, y, z; - Vector3() {} - Vector3( T _x, T _y, T _z) - { x = _x; y = _y; z = _z; } - - Vector3& operator= ( const Vector3& other ) - { - x = other.x; - y = other.y; - z = other.z; - return *this; - } - - /** - * returns the norm of the vector, that is, its length - */ - inline T norm() const { return SQRT( x * x + y * y + z * z ); } - - /** - * normalizes the vector, that is, scales it so that its norm - * becomes 1. - */ - void normalize() - { - T n = norm(); - if( n == 0.0 ) return; - x /= n; - y /= n; - z /= n; - } -}; - -/** -* Given a vector U, constructs two vectors v and w -* such that (U, v, w) is a direct orthogonal basis. +* Given a vector U, constructs two unit vectors v and w +* such that (U, v, w) is an orthogonal basis. * U is not supposed to be normalized. -* v and w are not getting normalized. +* +* Returns false if something went wrong. */ -template void construct_ortho_basis_given_first_vector( - const Vector3 &U, Vector3 & v, Vector3 & w ) -{ - if( U.norm() == 0 ) return; - - // let us first make a normalized copy of U - Vector3 u = U; - u.normalize(); - - // first we want to set v to be non-colinear to u - v = u; - - if( ! approx_equal( v.x, v.y, 0.1 ) ) - { - T tmp = v.x; - v.x = v.y; - v.y = tmp; - } - else if( ! approx_equal( v.y, v.z, 0.1 ) ) - { - T tmp = v.z; - v.z = v.y; - v.y = tmp; - } - else // the 3 coords of v are approximately equal - { // which implies that v is not colinear to (0,0,1) - v = Vector3( 0, 0, 1 ); - } - - // now, v is not colinear to u. We compute its dot product with u - T u_dot_v = u.x * v.x + u.y * v.y + u.z * v.z; - - // now we change v so that it becomes orthogonal to u - v.x -= u.x * u_dot_v; - v.y -= u.y * u_dot_v; - v.z -= u.z * u_dot_v; - - // now that u and v are orthogonal, w can be constructed as - // their crossed product - w.x = u.y * v.z - u.z * v.y; - w.y = u.z * v.x - u.x * v.z; - w.z = u.x * v.y - u.y * v.x; -} +bool createOrthoBasisGivenFirstVector( const OpenBabel::vector3 &U, OpenBabel::vector3 & v, OpenBabel::vector3 & w ); /** * This is an abstract base class for an OpenGL vertex array. @@ -217,33 +139,45 @@ template void construct_ortho_basis_given_first_vector( class VertexArray { protected: + + struct Vector + { + GLfloat x, y, z; + }; + + Vector *m_vertexBuffer; + Vector *m_normalBuffer; + unsigned short *m_indexBuffer; GLenum m_mode; - Vector3 *m_vertexBuffer; - Vector3 *m_normalBuffer; int m_vertexCount; - unsigned short *m_indexBuffer; int m_indexCount; GLuint m_displayList; - + bool m_hasIndexBuffer; + bool m_hasSeparateNormalBuffer; + bool m_isValid; + virtual int computeVertexCount() = 0; - virtual int computeIndexCount() = 0; + virtual int computeIndexCount() { return 0; } virtual void buildBuffers() = 0; - virtual bool allocateBuffers(); - virtual void compileDisplayListIfNeeded(); + bool allocateBuffers(); + void freeBuffers(); + void compileDisplayList(); - virtual void initialize(); + void initialize(); public: - VertexArray(); + VertexArray( GLenum mode, + bool hasIndexBuffer, + bool hasSeparateNormalBuffer ); virtual ~VertexArray(); - virtual void do_draw(); - virtual inline void draw() + void do_draw(); + inline void draw() { #ifdef USE_DISPLAY_LISTS - glCallList( m_displayList ); + if( m_isValid ) glCallList( m_displayList ); #else - do_draw(); + if( m_isValid ) do_draw(); #endif } }; @@ -262,7 +196,6 @@ class Sphere : public VertexArray protected: int m_detail; - GLfloat m_radius; virtual int computeVertexCount(); virtual int computeIndexCount(); virtual void buildBuffers(); @@ -270,8 +203,8 @@ class Sphere : public VertexArray public: Sphere(); virtual ~Sphere() {} - virtual void setup( int detail, GLfloat radius ); - virtual void drawScaled( GLfloat radius ); + virtual void setup( int detail ); + virtual void draw( const OpenBabel::vector3 ¢er, double radius ); }; /** @@ -283,16 +216,16 @@ class Cylinder : public VertexArray { protected: int m_faces; - GLfloat m_radius; virtual int computeVertexCount(); - virtual int computeIndexCount(); virtual void buildBuffers(); public: Cylinder(); virtual ~Cylinder() {} - virtual void setup( int detail, GLfloat radius ); + virtual void setup( int detail ); + virtual void draw( const OpenBabel::vector3 &end1, const OpenBabel::vector3 &end2, + double radius, int order = 1, double shift = 0.0 ); }; /** This is a helper class for TextRenderer, and should probably never be @@ -326,7 +259,8 @@ class CharRenderer public: CharRenderer(); ~CharRenderer(); - bool initialize( QChar c, const QFont &font ); + bool initialize( QChar c, const QFont &font, + GLenum textureTarget ); inline void draw() { glCallList( m_displayList ); @@ -452,12 +386,14 @@ class TextRenderer ///{ Members used to remember the OpenGL state in order to be able to restore it after rendering. See do_end(). GLboolean m_wasEnabled_LIGHTING; - GLboolean m_wasEnabled_TEXTURE_2D; + GLboolean m_wasEnabled_textureTarget; GLboolean m_wasEnabled_FOG; GLboolean m_wasEnabled_BLEND; GLboolean m_wasEnabled_DEPTH_TEST; ///} + GLenum m_textureTarget; + /** * Stores the relevant part of the OpenGL state, and prepares * for rendering diff --git a/kalzium/kalziumglwidget.cpp b/kalzium/kalziumglwidget.cpp index 79976d9..9432336 100644 --- a/kalzium/kalziumglwidget.cpp +++ b/kalzium/kalziumglwidget.cpp @@ -12,7 +12,6 @@ * * ***************************************************************************/ #include "kalziumglwidget.h" -#include "kalziumglhelperclasses.h" #include @@ -21,16 +20,14 @@ #include #include -#include - #ifdef USE_FPS_COUNTER #include #endif #include -#include +using namespace KalziumGLHelpers; using namespace OpenBabel; KalziumGLWidget::KalziumGLWidget( QWidget * parent ) @@ -45,18 +42,13 @@ KalziumGLWidget::KalziumGLWidget( QWidget * parent ) m_inZoom = false; m_inMeasure = false; + slotSetMolStyle( 0 ); + QFont f; f.setStyleHint( QFont::SansSerif, QFont::PreferAntialias ); m_textRenderer.setup( this, f ); - - - ChooseStylePreset( PRESET_SPHERES_AND_BICOLOR_BONDS ); setMinimumSize( 100,100 ); - - QTimer *timer = new QTimer( this ); - connect( timer, SIGNAL( timeout() ), this, SLOT( rotate() ) ); - timer->start( 50 ); } KalziumGLWidget::~KalziumGLWidget() @@ -77,9 +69,9 @@ void KalziumGLWidget::initializeGL() glGetDoublev( GL_MODELVIEW_MATRIX, m_RotationMatrix ); glPopMatrix(); - //glEnable( GL_RESCALE_NORMAL_EXT ); - - glEnable(GL_LIGHT0); + glEnable( GL_NORMALIZE ); + glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); GLfloat ambientLight[] = { 0.4, 0.4, 0.4, 1.0 }; GLfloat diffuseLight[] = { 0.8, 0.8, 0.8, 1.0 }; @@ -95,48 +87,53 @@ void KalziumGLWidget::initializeGL() glFogfv( GL_FOG_COLOR, fogColor ); glFogi( GL_FOG_MODE, GL_LINEAR ); glFogf( GL_FOG_DENSITY, 0.45 ); - glFogf( GL_FOG_START, 2.7 * m_molRadius ); - glFogf( GL_FOG_END, 5.0 * m_molRadius ); + glFogf( GL_FOG_START, 2.7 * getMolRadius() ); + glFogf( GL_FOG_END, 5.0 * getMolRadius() ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 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 ); } void KalziumGLWidget::paintGL() { if( ! m_molecule ) { - glColor3f( 0.0, 1.0, 0.6 ); glClear( GL_COLOR_BUFFER_BIT ); - m_textRenderer.print( 20, height() - 40, i18n("Please load a molecule") ); + glColor3f( 0.0, 1.0, 0.6 ); + m_textRenderer.print( 20, height() - 40, + i18n("Please load a molecule") ); return; } glMatrixMode( GL_PROJECTION ); glLoadIdentity(); - gluPerspective( 40.0, float( width() ) / height(), m_molRadius, 5.0 * (m_molRadius + atomRadius ())); + gluPerspective( 40.0, float( width() ) / height(), + getMolRadius(), 5.0 * getMolRadius() ); glMatrixMode( GL_MODELVIEW ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // set up the camera glLoadIdentity(); - GLTRANSLATE ( 0.0, 0.0, -3.0 * (m_molRadius + atomRadius () ) ); - GLMULTMATRIX ( m_RotationMatrix ); + glTranslated ( 0.0, 0.0, -3.0 * getMolRadius() ); + glMultMatrixd ( m_RotationMatrix ); // set up fog - if( m_useFog == true ) + if( m_useFog ) { glEnable( GL_FOG ); GLfloat fogColor[] = { 0.0, 0.0, 0.0, 1.0 }; glFogfv( GL_FOG_COLOR, fogColor ); glFogi( GL_FOG_MODE, GL_LINEAR ); glFogf( GL_FOG_DENSITY, 0.45 ); - glFogf( GL_FOG_START, 2.7 * ( m_molRadius + atomRadius() ) ); - glFogf( GL_FOG_END, 5.0 * ( m_molRadius + atomRadius() ) ); + glFogf( GL_FOG_START, 2.7 * getMolRadius() ); + glFogf( GL_FOG_END, 5.0 * getMolRadius() ); } else glDisable( GL_FOG ); @@ -148,101 +145,9 @@ void KalziumGLWidget::paintGL() glNewList( m_displayList, GL_COMPILE ); #endif - // prepare for rendering the spheres - if( m_atomStyle == ATOM_SPHERE ) - { - glEnable( GL_LIGHTING ); - } - else glDisable( GL_LIGHTING ); + renderAtoms(); + renderBonds(); - if( m_atomStyle != ATOM_DISABLED ) - { - // render the atoms - if( m_atomStyle == ATOM_SPHERE ) - { - FOR_ATOMS_OF_MOL( a, m_molecule ) - { - GLFLOAT x = (GLFLOAT) a->GetX(); - GLFLOAT y = (GLFLOAT) a->GetY(); - GLFLOAT z = (GLFLOAT) a->GetZ(); - - Color c = getAtomColor( &*a ); - - drawSphere( - x, y, z, - atomRadius(), - c); - } - } - } - - // prepare for rendering the bonds - switch( m_bondStyle ) - { - case BOND_LINE: - glDisable( GL_LIGHTING ); - break; - - case BOND_CYLINDER_GRAY: - case BOND_CYLINDER_BICOLOR: - glEnable( GL_LIGHTING ); - break; - case BOND_DISABLED: break; - } - - if( m_bondStyle != BOND_DISABLED ) - { - // render the bonds - FOR_BONDS_OF_MOL( bond, m_molecule ) - { - GLFLOAT x1 = (GLFLOAT) - static_cast(bond->GetBgn())->GetX(); - GLFLOAT y1 = (GLFLOAT) - static_cast(bond->GetBgn())->GetY(); - GLFLOAT z1 = (GLFLOAT) - static_cast(bond->GetBgn())->GetZ(); - GLFLOAT x2 = (GLFLOAT) - static_cast(bond->GetEnd())->GetX(); - GLFLOAT y2 = (GLFLOAT) - static_cast(bond->GetEnd())->GetY(); - GLFLOAT z2 = (GLFLOAT) - static_cast(bond->GetEnd())->GetZ(); - - GLFLOAT x3 = (x1 + x2) / 2; - GLFLOAT y3 = (y1 + y2) / 2; - GLFLOAT z3 = (z1 + z2) / 2; - - Color c1, c2; - c1 = getAtomColor( static_cast(bond->GetBgn()) ); - c2 = getAtomColor( static_cast(bond->GetEnd()) ); - Color gray( 0.5, 0.5, 0.5 ); - - switch( m_bondStyle ) - { - case BOND_LINE: - glBegin( GL_LINES ); - c1.apply(); - glVertex3f( x1, y1, z1 ); - glVertex3f( x3, y3, z3 ); - c2.apply(); - glVertex3f( x3, y3, z3 ); - glVertex3f( x2, y2, z2 ); - glEnd(); - break; - - case BOND_CYLINDER_GRAY: - drawBond( x1, y1, z1, x2, y2, z2, gray ); - break; - - case BOND_CYLINDER_BICOLOR: - drawBond( x1, y1, z1, x3, y3, z3, c1 ); - drawBond( x2, y2, z2, x3, y3, z3, c2 ); - break; - - case BOND_DISABLED: break; - } - } - } #ifdef USE_DISPLAY_LISTS glEndList(); m_haveToRecompileDisplayList = false; @@ -250,34 +155,63 @@ void KalziumGLWidget::paintGL() glCallList( m_displayList ); #endif - // now, paint a semitransparent sphere around the selected atoms - if( m_selectedAtoms.count() > 0 )//there are items selected + renderSelection(); + +#ifdef USE_FPS_COUNTER + FPSCounter(); + update(); +#endif +} + +void KalziumGLWidget::renderAtoms() +{ + if( m_molStyle.m_atomStyle == MolStyle::ATOMS_DISABLED ) return; + + FOR_ATOMS_OF_MOL( atom, m_molecule ) { - Color c( 0.4, 0.4, 1.0, 0.7 ); + drawAtom( &*atom ); + } +} - GLFLOAT radius = m_molMinBondLength * 0.35; - const GLFLOAT min_radius = (GLFLOAT) atomRadius () * 1.25; - if( radius < min_radius ) radius = min_radius; +void KalziumGLWidget::renderBonds() +{ + if( m_molStyle.m_bondStyle == MolStyle::BONDS_DISABLED ) return; - foreach(OpenBabel::OBAtom* atom, m_selectedAtoms) - {//iterate through all OBAtoms and highlight one after eachother - GLFLOAT x = (GLFLOAT) atom->GetX(); - GLFLOAT y = (GLFLOAT) atom->GetY(); - GLFLOAT z = (GLFLOAT) atom->GetZ(); + // render the bonds + FOR_BONDS_OF_MOL( bond, m_molecule ) + { + drawBond( &*bond ); + } +} - glEnable( GL_BLEND ); +void KalziumGLWidget::renderSelection() +{ +/* if( ! m_selectedAtoms.count() ) return; + + Color c( 0.4, 0.4, 1.0, 0.7 ); - glEnable( GL_LIGHTING ); + GLdouble radius = m_molMinBondLength * 0.35; + const GLdouble min_radius = (GLdouble) atomRadius () * 1.25; + if( radius < min_radius ) radius = min_radius; - drawSphere( - x, y, z, - radius, - c); + glEnable( GL_BLEND ); + glEnable( GL_LIGHTING ); +*/ +/* foreach(OpenBabel::OBAtom* atom, m_selectedAtoms) + {//iterate through all OBAtoms and highlight one after eachother - glDisable( GL_BLEND ); - } + drawSphere( + x, y, z, + radius, + c); } +*/ + + glDisable( GL_BLEND ); +} +void KalziumGLWidget::FPSCounter() +{ #ifdef USE_FPS_COUNTER QTime t; @@ -303,15 +237,13 @@ void KalziumGLWidget::paintGL() s = QString::number( 1000 * frames / double( new_time - old_time ), 'f', 1 ); - s += " FPS"; + s += QString(" FPS"); frames = 0; old_time = new_time; } glColor3f( 1.0, 1.0, 0.0 ); m_textRenderer.print( 20, 20, s ); - - update(); #endif } @@ -385,94 +317,91 @@ void KalziumGLWidget::rotate( ) void KalziumGLWidget::setupObjects() { - int sphere_detail, cylinder_faces; + int sphere_detail = 1, cylinder_faces = 2; - if( m_atomRadiusCoeff < 0.05) sphere_detail = 1; - else if( m_atomRadiusCoeff < 0.30) sphere_detail = 2; - else sphere_detail = 3; + double typicalAtomRadius = m_molStyle.getAtomRadius( 6 ); + double typicalBondRadius = m_molStyle.m_singleBondRadius; - sphere_detail *= ( m_detail + 1 ); - - if( m_bondRadiusCoeff < 0.02) cylinder_faces = 4; - else if( m_bondRadiusCoeff < 0.10) cylinder_faces = 6; - else cylinder_faces = 8; + if( m_molStyle.m_atomStyle != MolStyle::ATOMS_DISABLED ) + { + if( typicalAtomRadius < 0.50 ) + sphere_detail = 2 + 2 * m_detail; + else if( typicalAtomRadius < 1.00 ) + sphere_detail = 3 + 2 * m_detail; + else sphere_detail = 4 + 3 * m_detail; + } - cylinder_faces *= ( m_detail + 1 ); + if( m_molStyle.m_bondStyle != MolStyle::BONDS_DISABLED ) + { + if( typicalBondRadius < 0.10 ) + cylinder_faces = 6 + 6 * m_detail; + else if( typicalBondRadius < 0.20 ) + cylinder_faces = 8 + 8 * m_detail; + else cylinder_faces = 10 + 8 * m_detail; + } - m_sphere.setup( sphere_detail, atomRadius() ); - m_cylinder.setup( cylinder_faces, bondRadius() ); + m_sphere.setup( sphere_detail ); + m_cylinder.setup( cylinder_faces ); } -void KalziumGLWidget::drawSphere( GLdouble x, GLdouble y, GLdouble z, - GLfloat radius, Color &color ) +void KalziumGLWidget::drawAtom( OBAtom *atom ) { - color.applyAsMaterials(); - - glPushMatrix(); - glTranslated( x, y, z ); - m_sphere.drawScaled( radius ); - glPopMatrix(); + Color( atom ).applyAsMaterials(); + m_sphere.draw( atom->GetVector(), m_molStyle.getAtomRadius( atom ) ); } -void KalziumGLWidget::drawBond( FLOAT x1, FLOAT y1, FLOAT z1, - FLOAT x2, FLOAT y2, FLOAT z2, Color &color ) +void KalziumGLWidget::drawBond( OBBond *bond ) { - color.applyAsMaterials(); + OBAtom *atom1 = static_cast( bond->GetBgn() ); + OBAtom *atom2 = static_cast( bond->GetEnd() ); + + vector3 v1 = atom1->GetVector(); + vector3 v2 = atom2->GetVector(); + vector3 v3 = ( v1 + v2 ) / 2; + + int order; + if( bond->IsSingle() ) order = 1; + else if( bond->IsDouble() ) order = 2; + else if( bond->IsTriple() ) order = 3; + else + { + order = bond->GetBondOrder(); + if( order > 12 ) // probably a bogus molecule file! + // according to the element.txt file in OB, + // no element can have more than 12 bonds + { + order = 1; + kDebug()<<"Umm, some bond pretends to have " + "order "<GetBondOrder()<parentWidget()->close(); + } + } - // the "axis vector" of the cylinder - Vector3 axis( x2 - x1, y2 - y1, z2 - z1 ); - - // find two vectors v, w such that (axis,v,w) is an orthogonal basis. - Vector3 v, w; - construct_ortho_basis_given_first_vector( axis, v, w ); - - // normalize v and w. We DON'T want to normalize axis - v.normalize(); - w.normalize(); - - // construct the 4D transformation matrix - FLOAT matrix[16]; - - // column 1 - matrix[0] = v.x; - matrix[1] = v.y; - matrix[2] = v.z; - matrix[3] = 0.0; - - // column 2 - matrix[4] = w.x; - matrix[5] = w.y; - matrix[6] = w.z; - matrix[7] = 0.0; - - // column 3 - matrix[8] = axis.x; - matrix[9] = axis.y; - matrix[10] = axis.z; - matrix[11] = 0.0; - - // column 4 - matrix[12] = x1; - matrix[13] = y1; - matrix[14] = z1; - matrix[15] = 1.0; - - //now we can do the actual drawing ! - glPushMatrix(); - GLMULTMATRIX( matrix ); - m_cylinder.draw(); - glPopMatrix(); -} + double radius; + if( order == 1 ) radius = m_molStyle.m_singleBondRadius; + else radius = m_molStyle.m_multipleBondRadius; + switch( m_molStyle.m_bondStyle ) + { + case MolStyle::BONDS_GRAY: + Color( 0.5, 0.5, 0.5 ).applyAsMaterials(); + m_cylinder.draw( v1, v2, radius, order, + m_molStyle.m_multipleBondShift ); + break; -inline GLFLOAT KalziumGLWidget::bondRadius() -{ - return m_bondRadiusCoeff * m_molMinBondLength; - -} -inline GLFLOAT KalziumGLWidget::atomRadius() -{ - return m_atomRadiusCoeff * m_molMinBondLength; + case MolStyle::BONDS_USE_ATOMS_COLORS: + Color( atom1 ).applyAsMaterials(); + m_cylinder.draw( v1, v3, radius, order, + m_molStyle.m_multipleBondShift ); + Color( atom2 ).applyAsMaterials(); + m_cylinder.draw( v2, v3, radius, order, + m_molStyle.m_multipleBondShift ); + break; + + default: break; + } } void KalziumGLWidget::slotZoomIn() @@ -503,47 +432,33 @@ void KalziumGLWidget::slotSetMolecule( OpenBabel::OBMol* molecule ) updateGL(); } -void KalziumGLWidget::ChooseStylePreset( StylePreset stylePreset ) +void KalziumGLWidget::slotSetMolStyle( int style ) { - switch( stylePreset ) + switch( style ) { - case PRESET_LINES: - m_atomStyle = ATOM_DISABLED; - m_bondStyle = BOND_LINE; - m_atomRadiusCoeff = 0.0; - m_bondRadiusCoeff = 0.0; + case 0: // sticks-style + m_molStyle.setup( MolStyle::BONDS_USE_ATOMS_COLORS, + MolStyle::ATOMS_USE_FIXED_RADIUS, + 0.20, 0.06, 0.14, 0.20 ); break; - case PRESET_STICKS: - m_atomStyle = ATOM_SPHERE; - m_bondStyle = BOND_CYLINDER_BICOLOR; - m_atomRadiusCoeff = 0.13; - m_bondRadiusCoeff = 0.13; + case 1: // atoms: smaller van der Waals, bonds: gray + m_molStyle.setup( MolStyle::BONDS_GRAY, + MolStyle::ATOMS_USE_VAN_DER_WAALS_RADIUS, + 0.08, 0.08, 0.14, 0.20 ); break; - case PRESET_SPHERES_AND_GRAY_BONDS: - m_atomStyle = ATOM_SPHERE; - m_bondStyle = BOND_CYLINDER_GRAY; - m_atomRadiusCoeff = 0.20; - m_bondRadiusCoeff = 0.05; + case 2: // atoms: smaller van der Waals, bonds: use atom colors + m_molStyle.setup( MolStyle::BONDS_USE_ATOMS_COLORS, + MolStyle::ATOMS_USE_VAN_DER_WAALS_RADIUS, + 0.08, 0.08, 0.14, 0.20 ); break; - case PRESET_SPHERES_AND_BICOLOR_BONDS: - m_atomStyle = ATOM_SPHERE; - m_bondStyle = BOND_CYLINDER_BICOLOR; - m_atomRadiusCoeff = 0.20; - m_bondRadiusCoeff = 0.05; + case 3: // atoms: real van der Waals, bonds: disabled + m_molStyle.setup( MolStyle::BONDS_DISABLED, + MolStyle::ATOMS_USE_VAN_DER_WAALS_RADIUS, + 0.00, 0.00, 0.00, 1.00 ); break; - case PRESET_BIG_SPHERES: - m_atomStyle = ATOM_SPHERE; - m_bondStyle = BOND_DISABLED; - m_atomRadiusCoeff = 1.0; - m_bondRadiusCoeff = 0.0; - break; - } -} - -void KalziumGLWidget::slotChooseStylePreset( int stylePreset ) -{ - ChooseStylePreset( (StylePreset) stylePreset ); + default: break; + } m_haveToRecompileDisplayList = true; setupObjects(); updateGL(); @@ -557,7 +472,7 @@ void KalziumGLWidget::prepareMoleculeData() //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); + vector3 center( 0.0, 0.0, 0.0 ); int number_of_atoms = 0; FOR_ATOMS_OF_MOL( a, m_molecule ) { @@ -578,36 +493,19 @@ void KalziumGLWidget::prepareMoleculeData() // calculate the radius of the molecule // that is, the maximal distance between an atom of the molecule // and the center of the molecule - m_molRadius = 0.0; + m_molRadiusWithoutElectrons = 0.0; FOR_ATOMS_OF_MOL( a, m_molecule ) { - FLOAT x = (FLOAT) a->GetX(); - FLOAT y = (FLOAT) a->GetY(); - FLOAT z = (FLOAT) a->GetZ(); - FLOAT rad = SQRT(x*x + y*y + z*z); - if( rad > m_molRadius ) - m_molRadius = rad; + vector3 v = a->GetVector(); + double rad = v.length(); + if( rad > m_molRadiusWithoutElectrons ) + m_molRadiusWithoutElectrons = rad; } +} - // calculate the length of the shortest bond, of the longest bond - m_molMinBondLength = 2 * m_molRadius; - m_molMaxBondLength = 0.0; - FOR_BONDS_OF_MOL( b, m_molecule ) - { - FLOAT x1 = (FLOAT) static_cast(b->GetBgn())->GetX(); - FLOAT y1 = (FLOAT) static_cast(b->GetBgn())->GetY(); - FLOAT z1 = (FLOAT) static_cast(b->GetBgn())->GetZ(); - FLOAT x2 = (FLOAT) static_cast(b->GetEnd())->GetX(); - FLOAT y2 = (FLOAT) static_cast(b->GetEnd())->GetY(); - FLOAT z2 = (FLOAT) static_cast(b->GetEnd())->GetZ(); - FLOAT len = SQRT ( (x1 - x2) * (x1 - x2) - + (y1 - y2) * (y1 - y2) - + (z1 - z2) * (z1 - z2) ); - if( len > m_molMaxBondLength ) - m_molMaxBondLength = len; - if( len < m_molMinBondLength ) - m_molMinBondLength = len; - } +double KalziumGLWidget::getMolRadius() +{ + return m_molRadiusWithoutElectrons + m_molStyle.getAtomRadius( 6 ); } void KalziumGLWidget::slotSetDetail( int detail ) @@ -619,17 +517,6 @@ void KalziumGLWidget::slotSetDetail( int detail ) updateGL(); } -Color& KalziumGLWidget::getAtomColor( OpenBabel::OBAtom* atom ) -{ - // thanks to Geoffrey Hutchison from OpenBabel for - // this simplified getAtomColor method - static Color c; - 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; -} - void KalziumGLWidget::slotAtomsSelected( QList atoms ) { kDebug() << "KalziumGLWidget::slotAtomsSelected() with " << atoms.count() << " atoms" << endl; diff --git a/kalzium/kalziumglwidget.h b/kalzium/kalziumglwidget.h index 87bda6e..bbd1246 100644 --- a/kalzium/kalziumglwidget.h +++ b/kalzium/kalziumglwidget.h @@ -18,7 +18,6 @@ #include #include #include -#include using namespace KalziumGLHelpers; @@ -55,11 +54,10 @@ class KalziumGLWidget : public QGLWidget QPoint m_lastDraggingPosition; - /** * Stores the rotation that is applied to the model. */ - GLFLOAT m_RotationMatrix[16]; + GLdouble m_RotationMatrix[16]; /** * The molecule which is displayed @@ -67,34 +65,10 @@ class KalziumGLWidget : public QGLWidget OpenBabel::OBMol* m_molecule; /** - * approximate radius of the molecule - */ - GLFLOAT m_molRadius; - - /** - * length of the shortest bond in the molecule - */ - GLFLOAT m_molMinBondLength; - - /** - * length of the longest bond in the molecule - */ - GLFLOAT m_molMaxBondLength; - - /** - * The coefficient set by the user, determining the - * radius of atoms. - * WARNING: its meaning has just changed! (june 17) - * Now the actual radius is proportional to - * m_atomRadiusCoeff. - */ - float m_atomRadiusCoeff; - - /** - * The coefficient set by the user, determining the - * radius (that is, half the thickness) of bonds. + * approximate radius of the molecule, + * without the electrons. */ - float m_bondRadiusCoeff; + GLdouble m_molRadiusWithoutElectrons; /** * The detail-grade from 0 to 2. @@ -112,36 +86,9 @@ class KalziumGLWidget : public QGLWidget QList m_selectedAtoms; /** - * The style in which the atoms are rendered. - */ - enum AtomStyle - { - ATOM_DISABLED, - ATOM_SPHERE - } m_atomStyle; - - /** - * The style in which the bonds are rendered. + * The style in which the molecule is rendered */ - enum BondStyle - { - BOND_DISABLED, - BOND_LINE, - BOND_CYLINDER_GRAY, - BOND_CYLINDER_BICOLOR - } m_bondStyle; - - /** - * Some style presets - */ - enum StylePreset - { - PRESET_LINES, - PRESET_STICKS, - PRESET_SPHERES_AND_GRAY_BONDS, - PRESET_SPHERES_AND_BICOLOR_BONDS, - PRESET_BIG_SPHERES - }; + MolStyle m_molStyle; public: /** @@ -198,10 +145,10 @@ class KalziumGLWidget : public QGLWidget void slotSetDetail( int detail ); /** - * Chooses the style of rendering among some presets - * @param stylePreset the wanted style preset + * Sets the molecule style + * @param style the wanted molecule style */ - void slotChooseStylePreset( int stylePreset ); + void slotSetMolStyle( int style ); /** * The atoms @p atoms was selected by the user @@ -243,6 +190,10 @@ class KalziumGLWidget : public QGLWidget * This method does the painting. Automatically called by Qt */ virtual void paintGL(); + virtual void renderAtoms(); + virtual void renderBonds(); + virtual void renderSelection(); + virtual void FPSCounter(); /** * This method is called by Qt whenever the widget is resized. @@ -259,52 +210,12 @@ class KalziumGLWidget : public QGLWidget * it. */ void prepareMoleculeData(); - - virtual void drawSphere( - GLFLOAT x, - GLFLOAT y, - GLFLOAT z, - GLfloat radius, - Color &color ); - - /** - * This method draws a bond - * @param x1 - * @param y1 - * @param z1 - * @param x2 - * @param y2 - * @param z2 - * @param color - */ - virtual void drawBond( GLFLOAT x1, GLFLOAT y1, GLFLOAT z1, - GLFLOAT x2, GLFLOAT y2, GLFLOAT z2, - Color &color ); - - /** - * returns the radius ( = half-thickness ) with which the - * bonds are drawn - */ - inline GLFLOAT bondRadius(); - /** - * returns the radius with which the atoms are drawn - * (currently all atoms are drawn with the same radius. - * when we'll have a better model taking van der Waals radii - * into account, we'll remove this member). - */ - inline GLFLOAT atomRadius(); + double getMolRadius(); - /** - * Chooses the style of rendering among some presets - * @param stylePreset the wanted style preset - */ - virtual void ChooseStylePreset( StylePreset stylePreset ); + void drawAtom( OpenBabel::OBAtom *atom ); - /** - * returns the color which a given atom should be painted - */ - Color& getAtomColor( OpenBabel::OBAtom* atom ); + void drawBond( OpenBabel::OBBond *bond ); /** * recomputes the geometry of the geometric objects ( sphere,