/*
* Project: MoleCuilder
* Description: creates and alters molecular systems
* Copyright (C) 2010-2012 University of Bonn. All rights reserved.
* Copyright (C) 2013 Frederik Heber. All rights reserved.
*
*
* This file is part of MoleCuilder.
*
* MoleCuilder 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.
*
* MoleCuilder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MoleCuilder. If not, see .
*/
/*
* GLMoleculeObject.cpp
*
* This is based on the Qt3D example "teaservice", specifically meshobject.cpp.
*
* Created on: Aug 17, 2011
* Author: heber
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GLMoleculeObject.hpp"
#include
#include
#include
#include
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "Helpers/defs.hpp"
#include "Element/element.hpp"
#include "Element/periodentafel.hpp"
#include "World.hpp"
GLMoleculeObject::ElementMaterialMap GLMoleculeObject::ElementNoMaterialMap;
#include "CodePatterns/MemDebug.hpp"
QGLMaterial *GLMoleculeObject::m_hoverMaterial = NULL;
QGLMaterial *GLMoleculeObject::m_selectionMaterial = NULL;
QGLMaterial *GLMoleculeObject::m_selectionBoxMaterial = NULL;
QGLSceneNode *GLMoleculeObject::meshEmpty[GLMoleculeObject::DETAILTYPES_MAX];
QGLSceneNode *GLMoleculeObject::meshSphere[GLMoleculeObject::DETAILTYPES_MAX];
QGLSceneNode *GLMoleculeObject::meshCylinder[GLMoleculeObject::DETAILTYPES_MAX];
double GLMoleculeObject::detailMinDistance[GLMoleculeObject::DETAILTYPES_MAX] = {0, 15, 30, 42};
GLMoleculeObject::GLMoleculeObject(QGLSceneNode *mesh[], QObject *parent)
: QObject(parent)
{
//mesh->setParent(this);
for (int i=0;isetParent(this);
for (int i=0;isetParent(this);
for (int i=0;imainNode();
m_scaleX = 1.0f;
m_scaleY = 1.0f;
m_scaleZ = 1.0f;
m_rotationAngle = 0.0f;
m_effect = 0;
m_objectId = -1;
m_hovering = false;
m_selected = false;
m_material = 0;
initStaticMaterials();
}
GLMoleculeObject::~GLMoleculeObject()
{
}
void GLMoleculeObject::initialize(QGLView *view, QGLPainter *painter)
{
Q_UNUSED(painter);
if (m_objectId != -1)
view->registerObject(m_objectId, this);
}
/** Draws a box around the mesh.
*
*/
void GLMoleculeObject::drawSelectionBox(QGLPainter *painter)
{
painter->setFaceMaterial(QGL::AllFaces, m_selectionBoxMaterial);
QVector3DArray array;
qreal radius = 1.0;
array.append(-radius, -radius, -radius); array.append( radius, -radius, -radius);
array.append( radius, -radius, -radius); array.append( radius, radius, -radius);
array.append( radius, radius, -radius); array.append(-radius, radius, -radius);
array.append(-radius, radius, -radius); array.append(-radius, -radius, -radius);
array.append(-radius, -radius, radius); array.append( radius, -radius, radius);
array.append( radius, -radius, radius); array.append( radius, radius, radius);
array.append( radius, radius, radius); array.append(-radius, radius, radius);
array.append(-radius, radius, radius); array.append(-radius, -radius, radius);
array.append(-radius, -radius, -radius); array.append(-radius, -radius, radius);
array.append( radius, -radius, -radius); array.append( radius, -radius, radius);
array.append(-radius, radius, -radius); array.append(-radius, radius, radius);
array.append( radius, radius, -radius); array.append( radius, radius, radius);
painter->clearAttributes();
painter->setVertexAttribute(QGL::Position, array);
painter->draw(QGL::Lines, 24);
}
void GLMoleculeObject::draw(QGLPainter *painter, const QVector4D &cameraPlane)
{
// Position the model at its designated position, scale, and orientation.
painter->modelViewMatrix().push();
painter->modelViewMatrix().translate(m_position);
if (m_rotationAngle != 0.0f)
painter->modelViewMatrix().rotate(m_rotationAngle, m_rotationVector);
painter->modelViewMatrix().scale(m_scaleX, m_scaleY, m_scaleZ);
// Apply the material and effect to the painter.
QGLMaterial *material;
if (m_hovering)
material = m_hoverMaterial;
else if (m_selected)
material = m_selectionMaterial;
else
material = m_material;
ASSERT(material, "GLMoleculeObject::draw: chosen material is NULL");
painter->setColor(material->diffuseColor());
painter->setFaceMaterial(QGL::AllFaces, material);
if (m_effect)
painter->setUserEffect(m_effect);
else
painter->setStandardEffect(QGL::LitMaterial);
// Mark the object for object picking purposes.
int prevObjectId = painter->objectPickId();
if (m_objectId != -1)
painter->setObjectPickId(m_objectId);
// Draw the geometry mesh.
QVector4D pos4d(m_position, -1);
qreal distance = QVector4D::dotProduct(cameraPlane, pos4d);
if (distance > detailMinDistance[DETAIL_LOW])
m_mesh[DETAIL_LOW]->draw(painter);
else if (distance > detailMinDistance[DETAIL_MEDIUM])
m_mesh[DETAIL_MEDIUM]->draw(painter);
else if (distance > detailMinDistance[DETAIL_HIGH])
m_mesh[DETAIL_HIGH]->draw(painter);
else if (distance > detailMinDistance[DETAIL_HIGHEST])
m_mesh[DETAIL_HIGHEST]->draw(painter);
// Draw a box around the mesh, if selected.
if (m_selected)
drawSelectionBox(painter);
// Turn off the user effect, if present.
if (m_effect)
painter->setStandardEffect(QGL::LitMaterial);
// Revert to the previous object identifier.
painter->setObjectPickId(prevObjectId);
// Restore the modelview matrix.
painter->modelViewMatrix().pop();
}
bool GLMoleculeObject::event(QEvent *e)
{
// Convert the raw event into a signal representing the user's action.
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent *me = (QMouseEvent *)e;
if (me->button() == Qt::LeftButton)
emit pressed();
} else if (e->type() == QEvent::MouseButtonRelease) {
QMouseEvent *me = (QMouseEvent *)e;
if (me->button() == Qt::LeftButton) {
emit released();
if (me->x() >= 0) // Positive: inside object, Negative: outside.
emit clicked();
}
} else if (e->type() == QEvent::MouseButtonDblClick) {
emit doubleClicked();
} else if (e->type() == QEvent::Enter) {
m_hovering = true;
emit hoverChanged(this);
} else if (e->type() == QEvent::Leave) {
m_hovering = false;
emit hoverChanged(NULL);
}
return QObject::event(e);
}
/** Returns the ref to the Material for element No \a from the map.
*
* \note We create a new one if the element is missing.
*
* @param no element no
* @return ref to QGLMaterial
*/
QGLMaterial* GLMoleculeObject::getMaterial(size_t no)
{
ASSERT( (no > 0) && (no < MAX_ELEMENTS),
"GLMoleculeView::getMaterial() - Element no "+toString(no)+" is invalid.");
if (ElementNoMaterialMap.find(no) != ElementNoMaterialMap.end()){
// get present one
return ElementNoMaterialMap[no];
} else {
// create new one
LOG(1, "Creating new material for element "+toString(no)+".");
QGLMaterial *newmaterial = new QGLMaterial(NULL);
// create material for element
periodentafel *periode = World::getInstance().getPeriode();
const element *desiredelement = periode->FindElement(no);
ASSERT(desiredelement != NULL,
"GLMoleculeView::getMaterial() - desired element "+toString(no)+" not present in periodentafel.");
const unsigned char* color = desiredelement->getColor();
LOG(1, "Creating new material with color " << (int)color[0] << "," << (int)color[1] << "," << (int)color[2] << ".");
newmaterial->setAmbientColor( QColor((int)color[0], (int)color[1], (int)color[2]) );
newmaterial->setSpecularColor( QColor(60, 60, 60) );
newmaterial->setShininess( 128 );
ElementNoMaterialMap.insert( make_pair(no, newmaterial) );
return newmaterial;
}
}
/** Create the 3 materials shared by all objects.
*
*/
void GLMoleculeObject::initStaticMaterials()
{
if (!m_hoverMaterial){
m_hoverMaterial = new QGLMaterial(NULL);
m_hoverMaterial->setAmbientColor( QColor(0, 128, 128) );
m_hoverMaterial->setSpecularColor( QColor(60, 60, 60) );
m_hoverMaterial->setShininess( 128 );
}
if (!m_selectionMaterial){
m_selectionMaterial = new QGLMaterial(NULL);
m_selectionMaterial->setAmbientColor( QColor(255, 50, 50) );
m_selectionMaterial->setSpecularColor( QColor(60, 60, 60) );
m_selectionMaterial->setShininess( 128 );
}
if (!m_selectionBoxMaterial){
m_selectionBoxMaterial = new QGLMaterial(NULL);
m_selectionBoxMaterial->setAmbientColor( QColor(0, 0, 0) );
m_selectionBoxMaterial->setDiffuseColor( QColor(0, 0, 0) );
m_selectionBoxMaterial->setEmittedLight( QColor(155, 50, 50) );
}
}
/** Static function to be called when Materials have to be removed.
*
*/
void GLMoleculeObject::cleanMaterialMap()
{
for (ElementMaterialMap::iterator iter = ElementNoMaterialMap.begin();
!ElementNoMaterialMap.empty();
iter = ElementNoMaterialMap.begin()) {
delete iter->second;
ElementNoMaterialMap.erase(iter);
}
}
void GLMoleculeObject::setSelected(bool value)
{
if (value != m_selected){
m_selected = value;
emit selectionChanged();
}
}