Select Git revision
-
David Anderson authored
svn path=/trunk/boinc/; revision=4800
David Anderson authoredsvn path=/trunk/boinc/; revision=4800
pulsaranimationwidget.cpp 17.19 KiB
/******************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
* *
******************************************************************************/
#include "pulsaranimationwidget.h"
const float PulsarAnimationWidget::deg2rad = PI/180.0f;
PulsarAnimationWidget::PulsarAnimationWidget(QWidget *parent) :
QGLWidget(QGLFormat(QGL::AlphaChannel | QGL::SampleBuffers), parent),
m_frameTimer(),
m_pulseProfile(360, 0.0f)
{
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.0f;
m_cameraZoom = 15.0f;
m_cameraZoomLBound = 2.0f;
updateCameraPosition(m_mouseAngleH, m_mouseAngleV, m_cameraZoom);
updatePulseProfile();
}
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_simple.png"), GL_TEXTURE_2D, GL_RGBA);
m_backgroundTexture = bindTexture(QImage(":/textures/resources/texture_background_carina.jpg"), GL_TEXTURE_2D, GL_RGB);
// 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.475f, 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.475f, 0.0f, 3.0f, 32, 32);
glPopMatrix();
glPopMatrix();
// save current state
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, width(), 0, height(), 0.1f, 501.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// draw backdrop (independent parallel projection)
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_specular);
glTranslatef(0.0f, 0.0f, -501.0f);
drawTexture(QPointF(0.0f, 0.0f), m_backgroundTexture);
// restore original state
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
// draw orbital planes
if(m_cameraInteraction) {
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, translucent);
static float sizeOffset = 0.25f;
// companion's plane (only if not identical with pulsar's)
if(m_companionSemiMajorAxis != m_pulsarSemiMajorAxis) {
glPushMatrix();
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
// separate them slightly in case of overlap
// based on ugly mass diff check (no collision detection)
// single known problematic pair: (m_c=1.4, m_p=1.5)
if((int)(10*(m_pulsarMass-m_companionMass)+0.5f) == 1){
glTranslatef(0.0f, 0.0f, 0.01f);
}
gluDisk(m_quadricCompanionOrbitPlane,
m_companionSemiMajorAxis - sizeOffset,
m_companionSemiMajorAxis + sizeOffset,
64, 1);
glPopMatrix();
}
// pulsar's plane
glPushMatrix();
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
gluDisk(m_quadricPulsarOrbitPlane,
m_pulsarSemiMajorAxis - sizeOffset,
m_pulsarSemiMajorAxis + sizeOffset,
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;
updatePulseProfile();
}
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;
updatePulseProfile();
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();
updatePulseProfile();
updateGL();
}
void PulsarAnimationWidget::setCompanionMass(const float mass)
{
m_companionMass = mass;
updateOrbitRadii();
updatePulseProfile();
updateGL();
}
void PulsarAnimationWidget::setPulsarMass(const float mass)
{
m_pulsarMass = mass;
updateOrbitRadii();
updatePulseProfile();
updateGL();
}
void PulsarAnimationWidget::setPulsarSpinFrequency(const float frequency)
{
m_pulsarRotationDelta = (360.0f * frequency) / m_framesPerSecond;
updatePulseProfile();
}
void PulsarAnimationWidget::setPulsarSpinAxisInclination(const int degrees)
{
m_pulsarSpinAxisInclination = degrees;
updatePulseProfile();
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();
}
void PulsarAnimationWidget::updatePulseProfile()
{
const double i = m_pulsarSpinAxisInclination;
const double y = m_pulsarMagneticAxisInclination;
const double deltaPhiOrb = m_orbitRotationDelta;
const double deltaPhiRot = m_pulsarRotationDelta;
const double rp = m_pulsarSemiMajorAxis;
const double xk = m_cameraPosZ;
const double yk = -m_cameraPosX;
const double zk = -m_cameraPosY;
const double cam = pow(xk,2) + pow(yk,2) + pow(zk,2);
const double alpha = -m_mouseAngleH;
const double delta = m_mouseAngleV;
const double gaussProfile = 0.012337;
for(int x = 0; x < 360; ++x) {
// determine angle between pulsar's magnetic axis and line of sight
const double phiOrb = x * deltaPhiOrb;
const double phiRot = x * deltaPhiRot;
double a = -sin(y) * sin(phiRot) * (xk + rp * cos(phiOrb)) \
+ (cos(i) * sin(y) * cos(phiRot) + sin(i) * cos(y)) * (yk + rp * sin(phiOrb)) \
- (sin(i) * sin(y) * cos(phiRot) - cos(i) * cos(y)) * zk;
double b = sqrt(pow(rp,2) + cam - 2 * sqrt(cam) * rp * cos(delta) * sin(alpha + phiOrb));
// determine pulse amplitude
double amp = exp(-2 * (1 - fabs(a/b)) / gaussProfile);
// store amplitude
m_pulseProfile[x] = (float) amp;
}
// propagate new profile
emit pulseProfileUpdated(m_pulseProfile);
}