/****************************************************************************** * Copyright (C) 2008 by Oliver Bock * * oliver.bock[AT]aei.mpg.de * * * * This file is part of PulsatingScience. * * * * PulsatingScience 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, version 3 of the License. * * * * PulsatingScience 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 PulsatingScience. If not, see . * * * ******************************************************************************/ #include "pulsaranimationwidget.h" const float PulsarAnimationWidget::deg2rad = PI/180.0f; PulsarAnimationWidget::PulsarAnimationWidget(QWidget *parent) : QGLWidget(QGLFormat(QGL::AlphaChannel | QGL::SampleBuffers), parent), m_frameTimer() { if(!format().directRendering()) { qWarning("Sorry, no direct rendering support for animation..."); } if(!format().doubleBuffer()) { qWarning("Sorry, no double buffering support for animation..."); } if(!format().rgba()) { qWarning("Sorry, no RGBA support for animation..."); } if(!format().alpha()) { qWarning("Sorry, no alpha channel support for animation..."); } if(!format().sampleBuffers()) { qWarning("Sorry, no multisampling support for animation..."); } connect(&m_frameTimer, SIGNAL(timeout()), this, SLOT(updateFrame())); m_quadricCompanionOrbitPlane = NULL; m_quadricCompanion = NULL; m_quadricPulsarOrbitPlane = NULL; m_quadricPulsar = NULL; m_quadricPulsarCone1 = NULL; m_quadricPulsarCone2 = NULL; m_quadricPulsarSpinAxis = NULL; m_quadricPulsarMagneticAxis = NULL; m_pulsarTexture = 0; m_backgroundTexture = 0; m_framesPerSecond = 25; resetParameters(); m_pulsarMass = 1.4f; m_companionMass = 1.4f; m_showRotationAxes = false; m_cameraInteraction = false; m_mouseLastX = 0; m_mouseLastY = 0; m_mouseAngleH = 90.0f; m_mouseAngleV = 30.f; m_cameraZoom = 15.0f; m_cameraZoomLBound = 2.0f; updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom); } PulsarAnimationWidget::~PulsarAnimationWidget() { if(m_quadricCompanionOrbitPlane) gluDeleteQuadric(m_quadricCompanionOrbitPlane); if(m_quadricCompanion) gluDeleteQuadric(m_quadricCompanion); if(m_quadricPulsarOrbitPlane) gluDeleteQuadric(m_quadricPulsarOrbitPlane); if(m_quadricPulsar) gluDeleteQuadric(m_quadricPulsar); if(m_quadricPulsarCone1) gluDeleteQuadric(m_quadricPulsarCone1); if(m_quadricPulsarCone2) gluDeleteQuadric(m_quadricPulsarCone2); if(m_quadricPulsarSpinAxis) gluDeleteQuadric(m_quadricPulsarSpinAxis); if(m_quadricPulsarMagneticAxis) gluDeleteQuadric(m_quadricPulsarMagneticAxis); if(m_pulsarTexture) deleteTexture(m_pulsarTexture); if(m_backgroundTexture) deleteTexture(m_backgroundTexture); } void PulsarAnimationWidget::initializeGL() { glClearColor(0.0, 0.0, 0.0, 0.0); glClearDepth(1.0f); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); GLfloat LightAmbient[] = { 0.3f, 0.3f, 0.3f, 1.0f }; GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat LightSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat LightPosition[] = { 0.0f, 0.0f, 3.0f, 1.0f }; GLfloat spot_direction[] = { 0.0, 0.0, -1.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient); glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular); glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 50.0); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction); glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 10.0); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); m_quadricCompanionOrbitPlane = gluNewQuadric(); m_quadricCompanion = gluNewQuadric(); m_quadricPulsarOrbitPlane = gluNewQuadric(); m_quadricPulsar = gluNewQuadric(); m_quadricPulsarCone1 = gluNewQuadric(); m_quadricPulsarCone2 = gluNewQuadric(); m_quadricPulsarSpinAxis = gluNewQuadric(); m_quadricPulsarMagneticAxis = gluNewQuadric(); gluQuadricNormals(m_quadricCompanionOrbitPlane, GLU_SMOOTH); gluQuadricNormals(m_quadricCompanion, GLU_SMOOTH); gluQuadricNormals(m_quadricPulsarOrbitPlane, GLU_SMOOTH); gluQuadricNormals(m_quadricPulsar, GLU_SMOOTH); gluQuadricNormals(m_quadricPulsarCone1, GLU_SMOOTH); gluQuadricNormals(m_quadricPulsarCone2, GLU_SMOOTH); gluQuadricNormals(m_quadricPulsarSpinAxis, GLU_SMOOTH); gluQuadricNormals(m_quadricPulsarMagneticAxis, GLU_SMOOTH); // load textures m_pulsarTexture = bindTexture(QImage(":/textures/resources/texture_pulsar.png"), GL_TEXTURE_2D, GL_RGBA); m_backgroundTexture = bindTexture(QImage(":/textures/resources/texture_background.png"), GL_TEXTURE_2D, GL_RGBA); // use mipmapped textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); // enable 2D mapping (s/t coordinates, sphere map won't rotate texture!) glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); } void PulsarAnimationWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, (GLfloat)w / (GLfloat)h, 0.1f, 500.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void PulsarAnimationWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(m_cameraPosX, m_cameraPosY, m_cameraPosZ, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // TODO: should be located elsewhere static GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 }; static GLfloat mat_diffuse[] = { 0.5, 0.5, 0.5, 1.0 }; static GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; static GLfloat low_shininess[] = { 2.5 }; static GLfloat translucent[] = { 1.0, 1.0, 1.0, 0.33 }; glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); // draw companion glPushMatrix(); glTranslatef(sin((m_orbitRotationAngle + 180) * deg2rad) * m_companionSemiMajorAxis, 0.0f, cos((m_orbitRotationAngle + 180) * deg2rad) * m_companionSemiMajorAxis); gluSphere(m_quadricCompanion, 1.0f, 32, 32); glPopMatrix(); // draw pulsar glPushMatrix(); glTranslatef(sin(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis, 0.0f, cos(m_orbitRotationAngle * deg2rad) * m_pulsarSemiMajorAxis); glPushMatrix(); glRotatef(m_pulsarSpinAxisInclination, 0.0f, 0.0f, 1.0f); glRotatef(m_pulsarRotationAngle, 0.0f, 1.0f, 0.0f); // draw spin axis if(m_showRotationAxes) { glPushMatrix(); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent); glRotatef(90.0f, 1.0f, 0.0f, 0.0f); glTranslatef(0.0f, 0.0f, -5.0f); gluCylinder(m_quadricPulsarSpinAxis, 0.05f, 0.05f, 10.0f, 32, 1); glPopMatrix(); } // create texture coordinates and enable texturing glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glBindTexture(GL_TEXTURE_2D, m_pulsarTexture); glEnable(GL_TEXTURE_2D); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); gluSphere(m_quadricPulsar, 1.0f, 32, 32); // disable texturing glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_2D); glPopMatrix(); // TODO: should be located elsewhere static GLfloat coneAmbient[] = { 1.0, 1.0, 0.0, 1.0 }; static GLfloat coneDiffuse[] = { 1.0, 1.0, 0.0, 1.0 }; static GLfloat coneSpecular[] = { 1.0, 1.0, 0.5, 1.0 }; glMaterialfv(GL_FRONT, GL_SPECULAR, coneSpecular); // first cone glPushMatrix(); glRotatef(90.0f, 1.0f, 0.0f, 0.0f); glRotatef(m_pulsarSpinAxisInclination, 0.0f, 1.0f, 0.0f); glRotatef(-m_pulsarRotationAngle - 90.0f, 0.0f, 0.0f, 1.0f); glRotatef(-m_pulsarMagneticAxisInclination, 1.0f, 0.0f, 0.0f); // draw magnetic axis (for both cones) if(m_showRotationAxes) { glPushMatrix(); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent); glTranslatef(0.0f, 0.0f, -5.0f); gluCylinder(m_quadricPulsarMagneticAxis, 0.05f, 0.05f, 10.0f, 32, 1); glPopMatrix(); } glTranslatef(0.0f, 0.0f, -4.0f); glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse); gluCylinder(m_quadricPulsarCone1, 0.5f, 0.0f, 3.0f, 32, 32); glPopMatrix(); // second cone glPushMatrix(); glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); glRotatef(-m_pulsarSpinAxisInclination, 0.0f, 1.0f, 0.0f); glRotatef(m_pulsarRotationAngle - 90.0f, 0.0f, 0.0f, 1.0f); glRotatef(m_pulsarMagneticAxisInclination, 1.0f, 0.0f, 0.0f); glTranslatef(0.0f, 0.0f, -4.0f); glMaterialfv(GL_FRONT, GL_AMBIENT, coneAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, coneDiffuse); gluCylinder(m_quadricPulsarCone2, 0.5f, 0.0f, 3.0f, 32, 32); glPopMatrix(); glPopMatrix(); // draw orbital planes if(m_cameraInteraction) { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent); // companion glPushMatrix(); glRotatef(90.0f, 1.0f, 0.0f, 0.0f); glTranslatef(0.0f, 0.0f, 0.0f); gluDisk(m_quadricCompanionOrbitPlane, m_companionSemiMajorAxis - 0.25f, m_companionSemiMajorAxis + 0.25f, 64, 1); glPopMatrix(); // pulsar glPushMatrix(); glRotatef(90.0f, 1.0f, 0.0f, 0.0f); glTranslatef(0.0f, 0.0f, 0.1f); gluDisk(m_quadricPulsarOrbitPlane, m_pulsarSemiMajorAxis - 0.25f, m_pulsarSemiMajorAxis + 0.25f, 64, 1); glPopMatrix(); } } void PulsarAnimationWidget::runAnimation() { m_frameTimer.start(1000.0f / m_framesPerSecond); } void PulsarAnimationWidget::pauseAnimation() { m_frameTimer.stop(); } void PulsarAnimationWidget::stopAnimation() { m_frameTimer.stop(); resetParameters(); updateGL(); } void PulsarAnimationWidget::updateFrame() { m_pulsarRotationAngle += m_pulsarRotationDelta; if(m_pulsarRotationAngle > 360) { m_pulsarRotationAngle = 0.0f; } m_orbitRotationAngle += m_orbitRotationDelta; if(m_orbitRotationAngle > 360) { m_orbitRotationAngle = 0.0f; } updateGL(); emit pulsarAnimationStep(m_pulsarRotationDelta); } void PulsarAnimationWidget::showRotationAxes(bool enabled) { m_showRotationAxes = enabled; updateGL(); } void PulsarAnimationWidget::mousePressEvent(QMouseEvent *event) { Q_UNUSED(event); m_cameraInteraction = true; updateGL(); } void PulsarAnimationWidget::mouseMoveEvent(QMouseEvent *event) { Qt::MouseButtons buttons = event->buttons(); if((buttons & Qt::LeftButton) == Qt::LeftButton) { if(m_mouseLastX != 0) { m_mouseAngleH -= (m_mouseLastX - event->x()) / 2; m_mouseAngleH = m_mouseAngleH < 360 ? m_mouseAngleH : 0; } if(m_mouseLastY != 0) { m_mouseAngleV -= (m_mouseLastY - event->y()) / 2; m_mouseAngleV = m_mouseAngleV < 90 ? m_mouseAngleV : 90; m_mouseAngleV = m_mouseAngleV > -90 ? m_mouseAngleV : -90; } m_mouseLastX = event->x(); m_mouseLastY = event->y(); } else if((buttons & Qt::RightButton) == Qt::RightButton) { if(m_mouseLastY != 0) { m_cameraZoom -= (m_mouseLastY - event->y()) / 2; m_cameraZoom = m_cameraZoom >= m_cameraZoomLBound ? m_cameraZoom : m_cameraZoomLBound; updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom); } m_mouseLastY = event->y(); } updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom); } void PulsarAnimationWidget::mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); m_mouseLastX = 0; m_mouseLastY = 0; m_cameraInteraction = false; updateGL(); } void PulsarAnimationWidget::updateCameraPosition(const int angleH, const int angleV, const float zoom) { m_cameraPosX = sin(angleH * deg2rad) * zoom; m_cameraPosY = sin(angleV * deg2rad) * zoom; m_cameraPosZ = cos(angleH * deg2rad) * cos(fabs(angleV * deg2rad)) * zoom; updateGL(); } void PulsarAnimationWidget::setFramePerSecond(const unsigned int fps) { m_framesPerSecond = fps; } void PulsarAnimationWidget::setPulsarSemiMajorAxis(const float length) { m_pulsarSemiMajorAxis = length; m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis; updateOrbitPeriod(); updateGL(); } void PulsarAnimationWidget::setCompanionMass(const float mass) { m_companionMass = mass; updateOrbitRadii(); updateGL(); } void PulsarAnimationWidget::setPulsarMass(const float mass) { m_pulsarMass = mass; updateOrbitRadii(); updateGL(); } void PulsarAnimationWidget::setPulsarSpinFrequency(const float frequency) { m_pulsarRotationDelta = (360.0f * frequency) / m_framesPerSecond; } void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees) { m_pulsarSpinAxisInclination = degrees; updateGL(); } void PulsarAnimationWidget::setPulsarMagneticAxisInclination(const int degrees) { m_pulsarMagneticAxisInclination = degrees; updateGL(); } void PulsarAnimationWidget::updateOrbitPeriod() { m_orbitalPeriod = 3.1553e7 * sqrt( (pow(m_pulsarSemiMajorAxis,3) * pow(m_pulsarMass+m_companionMass,2)) / pow(m_companionMass,3) ); // visual correction factor (increase orbital momentum) double visualCorrection = 1e-8; m_orbitRotationDelta = (360.0 / (m_orbitalPeriod*visualCorrection)) / m_framesPerSecond; } void PulsarAnimationWidget::updateOrbitRadii() { m_pulsarSemiMajorAxis = 1.0015e-5 * pow( (pow(m_orbitalPeriod,2) * pow(m_companionMass,3)) / pow(m_pulsarMass+m_companionMass,2), 1.0/3.0 ); m_companionSemiMajorAxis = (m_pulsarMass/m_companionMass) * m_pulsarSemiMajorAxis; emit pulsarSemiMajorAxisUpdated(m_pulsarSemiMajorAxis); } void PulsarAnimationWidget::resetParameters() { m_pulsarRotationAngle = 0.0f; m_orbitRotationAngle = 0.0f; emit pulsarAnimationReset(); }