Skip to content
Snippets Groups Projects
Select Git revision
  • 01841b0f4833ac21a97d02618e3949569796c78b
  • master default protected
2 results

test_knm.py

Blame
  • Starsphere.cpp 25.65 KiB
    /***************************************************************************
     *   Copyright (C) 2004 David Hammer, Eric Myers, Bruce Allen              *
     *   Copyright (C) 2008 Bernd Machenschalk                                 *
     *                                                                         *
     *   Copyright (C) 2008 by Oliver Bock                                     *
     *   oliver.bock[AT]aei.mpg.de                                             *
     *                                                                         *
     *   This file is part of Einstein@Home.                                   *
     *                                                                         *
     *   Einstein@Home 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 2 of the License.            *
     *                                                                         *
     *   Einstein@Home 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 Einstein@Home. If not, see <http://www.gnu.org/licenses/>. *
     *                                                                         *
     ***************************************************************************/
    
    #include "Starsphere.h"
    
    Starsphere::Starsphere() : AbstractGraphicsEngine()
    {
    	m_FontResource = 0;
    	m_FontLogo1 = 0;
    	m_FontLogo2 = 0;
    	m_FontHeader = 0;
    	m_FontText = 0;
    	
    	Axes=0, Stars=0, Constellations=0, Pulsars=0;
    	LLOmarker=0, LHOmarker=0, GEOmarker=0, VIRGOmarker=0;
    	sphGrid=0, SNRs=0, SearchMarker=0;
    
    	/**
    	 * Parameters and State info
    	 */
    	sphRadius = 5.5;
    	featureFlags = 0;
    
    	/**
    	 * Viewpoint (can be changed with mouse)
    	 */
    	viewpt_azimuth = 30.0;
    	viewpt_elev = 23.6;
    	viewpt_radius = 7.6;
    
    	wobble_amp = 37.0;
    	wobble_period = 17.0;
    	zoom_amp = 0.00;
    	zoom_period = 29.0;
    
    	rotation_offset = 0.0;
    	rotation_speed = 180.0;
    	
    	m_CurrentRightAscension = -1.0;
    	m_CurrentDeclination = -1.0;
    	m_RefreshSearchMarker = true;
    }
    
    Starsphere::~Starsphere()
    {
    	if(m_FontLogo1) delete m_FontLogo1;
    	if(m_FontLogo2) delete m_FontLogo2;
    	if(m_FontHeader) delete m_FontHeader;
    	if(m_FontText) delete m_FontText;
    }
    
    /**
     * sphVertex3D() creates a GL vertex in 3D sky sphere coordinates
     * sphVertex() creates a GL vertex on the surface of the sky sphere.
     * Use either like glVertex().
     */
    void Starsphere::sphVertex3D(GLfloat RAdeg, GLfloat DEdeg, GLfloat radius)
    {
    	GLfloat x, y, z;
    
    	x = radius * COS(DEdeg) * COS(RAdeg);
    	z = -radius * COS(DEdeg) * SIN(RAdeg);
    	y = radius * SIN(DEdeg);
    	glVertex3f(x, y, z);
    	return;
    }
    
    void Starsphere::sphVertex(GLfloat RAdeg, GLfloat DEdeg)
    {
    	sphVertex3D(RAdeg, DEdeg, sphRadius);
    }
    
    /**
     * Star Marker:
     * Makes a marker for one star at a given position and angular size.  
     */
    void Starsphere::star_marker(float RAdeg, float DEdeg, float size)
    {
    	glPointSize((GLfloat) size);
    	glBegin(GL_POINTS);
    	sphVertex((GLfloat) RAdeg, (GLfloat) DEdeg);
    	glEnd();
    	return;
    }
    
    /**
     *  Create Stars: markers for each star
     */
    void Starsphere::make_stars()
    {
    	GLfloat mag_size;
    	int i, j;
    	bool is_dupe;
    	int Ndupes=0;
    
    	// delete existing, create new (required for windoze)
    	if(Stars) glDeleteLists(Stars, 1);
    	Stars = glGenLists(1);
    	glNewList(Stars, GL_COMPILE);
    	
    		glColor3f(1.0, 1.0, 1.0);
    	
    		/**
    		 * At some point in the future star_info[][] will also contain
    		 * star magnitude and the marker size will vary with this.
    		 */
    		for (i=0; i < Nstars; i++) {
    			// same stars appear more than once in constallations so ignore dupes
    			is_dupe=false;
    			for (j=0; j< i; j++) {
    				if (star_info[j][0] == star_info[i][0] && star_info[j][0]
    				        == star_info[i][0]) {
    					is_dupe=true;
    					Ndupes++;
    					break;
    				}
    			}
    			if (!is_dupe) {
    				// mag_size = 0.05 + 0.50*rand()/RAND_MAX;
    				mag_size = 4.0;
    				star_marker(star_info[i][0], star_info[i][1], mag_size);
    			}
    		}
    		
    	glEndList();
    }
    
    /**
     *  Pulsar Markers:
     */
    void Starsphere::make_pulsars()
    {
    	GLfloat mag_size=3.0;
    	int i;
    
    	// delete existing, create new (required for windoze)
    	if(Pulsars) glDeleteLists(Pulsars, 1);
    	Pulsars = glGenLists(1);
    	glNewList(Pulsars, GL_COMPILE);
    	
    		glColor3f(0.80, 0.0, 0.85); // _P_ulsars are _P_urple
    	
    		for (i=0; i < Npulsars; i++) {
    			star_marker(pulsar_info[i][0], pulsar_info[i][1], mag_size);
    		}
    		
    	glEndList();
    }
    
    /**
     * Super Novae Remenants (SNRs):
     */
    void Starsphere::make_snrs()
    {
    	GLfloat mag_size=3.0;
    	int i;
    
    	// delete existing, create new (required for windoze)
    	if(SNRs) glDeleteLists(SNRs, 1);
    	SNRs = glGenLists(1);
    	glNewList(SNRs, GL_COMPILE);
    	
    		glColor3f(0.7, 0.176, 0.0); // _S_NRs are _S_ienna
    	
    		for (i=0; i < NSNRs; i++) {
    			star_marker(SNR_info[i][0], SNR_info[i][1], mag_size);
    		}
    		
    	glEndList();
    }
    
    /**
     * Create Constellations:
     * draws line links between pairs of stars in the list.
     */
    void Starsphere::make_constellations()
    {
    	GLint star_num=0;
    
    	// delete existing, create new (required for windoze)
    	if(Constellations) glDeleteLists(Constellations, 1);
    	Constellations = glGenLists(1);
    	glNewList(Constellations, GL_COMPILE);
    	
    		glLineWidth(1.0);
    		glColor3f(0.7, 0.7, 0.0); // light yellow
    	
    		glBegin(GL_LINES); // draws lines between *pairs* of vertices
    			for (star_num=0; star_num < Nstars; ++star_num) {
    				sphVertex(star_info[star_num][0], star_info[star_num][1]);
    				star_num++;
    				sphVertex(star_info[star_num][0], star_info[star_num][1]);
    			}
    		glEnd();
    		
    	glEndList();
    }
    
    /**
     * Create markers on sky sphere for LLO, LHO, and GEO
     * IFO corner positions are from Myers' personal GPS and are +/- 100m
     */
    
    /**
     * RAofZenith(time, longitude)
     *
     * Computes the Right Ascention of the zenith at a given time (from
     * the Unix epoch, in seconds) at a given Longitude (in degrees). From
     * 'The Cambridge Handbook of Physics Formulas', Graham Woan, 2003
     * edition, CUP.  (NOT the first edition), p177.
     */
    GLfloat Starsphere::RAofZenith(double T, GLfloat LONdeg)
    {
    
    	// unix epoch at 12h  1/1/2000
    	const double T_0 = 946728000.0;
    
    	//  UT seconds of the day
    	double T_s = fmod(T, 24.0*3600.0);
    
    	// Julian centuries since 12h 1/1/2000 and 0h today
    	double T_c = (T - T_s - T_0)/3155760000.0;
    
    	// GMST at 0h today in seconds
    	double GMST0 = 6.0*3600.0 + 41.0*60.0 + 50.54841 + (8640184.812866
    	        + 0.093104*T_c)*T_c;
    
    	// GMST now in seconds
    	double GMST = GMST0 + 1.002738*T_s;
    
    	// longitude defined as west positive
    	GLfloat alpha = (GMST/(24.0*3600.0))*360.0 - LONdeg;
    
    	return alpha;
    }
    
    /**
     * Draw the observatories at their zenith positions
     */
    void Starsphere::make_obs()
    {
    	GLfloat Lat, Lon; // Latitute/Longitude of IFO is
    	GLfloat RAdeg, DEdeg; // converted to RA/DEC of sky sphere position
    	GLfloat radius; // radius of sphere for obs
    
    	GLfloat arm_len_deg=3.000; // lenght of arms, in degrees (not to scale)
    	GLfloat h2=0.400; // slight offset for H2 arms
    
    	// get current time and UTC offset (for zenith position)
    	m_ObservatoryDrawTimeLocal = dtime();
    	time_t local = m_ObservatoryDrawTimeLocal;
    	tm *utc = gmtime(&local);
    	double utcOffset = difftime(local, mktime(utc));	
    	double observatoryDrawTimeGMT = m_ObservatoryDrawTimeLocal - utcOffset;
    
    	radius = 1.0*sphRadius; // radius of sphere on which they are drawn
    
    	float lineSize = 4.0;
    
    	/**
    	 * LIGO Livingston Observatory:
    	 */
    
    	Lat= 30.56377;
    	Lon= 90.77408;
    
    	RAdeg= RAofZenith(observatoryDrawTimeGMT, Lon);
    	DEdeg= Lat;
    
    	// delete existing, create new (required for windoze)
    	if(LLOmarker) glDeleteLists(LLOmarker, 1);
    	LLOmarker = glGenLists(1);
    	glNewList(LLOmarker, GL_COMPILE);
    	
    		glColor3f(0.0, 1.0, 0.0);
    		glLineWidth(lineSize);
    		
    		glBegin(GL_LINE_STRIP);
    			//  North/South arm:
    			sphVertex3D(RAdeg, DEdeg-arm_len_deg, radius);
    			sphVertex3D(RAdeg, DEdeg, radius);
    			// East/West arm:
    			sphVertex3D(RAdeg-arm_len_deg, DEdeg, radius);
    		glEnd();
    		
    		// arm joint H2
    		glPointSize((GLfloat) lineSize);
    		glBegin(GL_POINTS);
    			sphVertex3D(RAdeg, DEdeg, radius);
    		glEnd();
    		
    	glEndList();
    
    	/**
    	 * LIGO Hanford Observatory: H1 and H2
    	 */
    
    	Lat= 46.45510;
    	Lon= 119.40627;
    
    	RAdeg= RAofZenith(observatoryDrawTimeGMT, Lon);
    	DEdeg= Lat;
    
    	// delete existing, create new (required for windoze)
    	if(LHOmarker) glDeleteLists(LHOmarker, 1);
    	LHOmarker = glGenLists(1);
    	glNewList(LHOmarker, GL_COMPILE);
    	
    		glColor3f(0.0, 0.0, 1.0);
    		glLineWidth(lineSize);
    		
    		glBegin(GL_LINE_STRIP);
    			// North/South arm:
    			sphVertex3D(RAdeg, DEdeg+arm_len_deg, radius);
    			sphVertex3D(RAdeg, DEdeg, radius);
    			// East/West arm:
    			sphVertex3D(RAdeg-arm_len_deg, DEdeg, radius);
    		glEnd();
    		
    		glBegin(GL_LINE_STRIP);
    			// North/South arm, H2:
    			sphVertex3D(RAdeg-h2, DEdeg+arm_len_deg/2.0+h2/2.0, radius);
    			sphVertex3D(RAdeg-h2, DEdeg+h2/2.0, radius);
    			// East/West arm, H2;
    			sphVertex3D(RAdeg-arm_len_deg/2.0-h2, DEdeg+h2/2.0, radius);
    		glEnd();
    		
    		// arm joint H1
    		glPointSize((GLfloat) lineSize);
    		glBegin(GL_POINTS);
    			sphVertex3D(RAdeg, DEdeg, radius);
    		glEnd();
    		
    		// arm joint H2
    		glPointSize((GLfloat) lineSize);
    		glBegin(GL_POINTS);
    			sphVertex3D(RAdeg-h2, DEdeg+h2/2.0, radius);
    		glEnd();
    		
    	glEndList();
    
    	/**
    	 *  GEO600 Interferometer:
    	 */
    
    	Lat= 52.24452;
    	Lon= -9.80683;
    	arm_len_deg=1.50; // not to scale
    
    	RAdeg= RAofZenith(observatoryDrawTimeGMT, Lon);
    	DEdeg= Lat;
    
    	// delete existing, create new (required for windoze)
    	if(GEOmarker) glDeleteLists(GEOmarker, 1);
    	GEOmarker = glGenLists(1);
    	glNewList(GEOmarker, GL_COMPILE);
    	
    		glColor3f(1.0, 0.0, 0.0);
    		glLineWidth(lineSize);
    		
    		glBegin(GL_LINE_STRIP);
    			// North/South arm:
    			sphVertex3D(RAdeg, DEdeg+arm_len_deg, radius);
    			sphVertex3D(RAdeg, DEdeg, radius);
    			// West/East arm:
    			sphVertex3D(RAdeg+arm_len_deg, DEdeg, radius);
    		glEnd();
    		
    		// arm joint
    		glPointSize((GLfloat) lineSize);
    		glBegin(GL_POINTS);
    			sphVertex3D(RAdeg, DEdeg, radius);
    		glEnd();
    		
    	glEndList();
    	
    	/**
    	 *  VIRGO Interferometer:
    	 */
    
    	Lat= 43.63139;
    	Lon= -10.505;
    	arm_len_deg=3.000; // not to scale
    
    	RAdeg= RAofZenith(observatoryDrawTimeGMT, Lon);
    	DEdeg= Lat;
    
    	// delete existing, create new (required for windoze)
    	if(VIRGOmarker) glDeleteLists(VIRGOmarker, 1);
    	VIRGOmarker = glGenLists(1);
    	glNewList(VIRGOmarker, GL_COMPILE);
    	
    		glColor3f(1.0, 1.0, 1.0);
    		glLineWidth(lineSize);
    		
    		glBegin(GL_LINE_STRIP);
    			// North/South arm:
    			sphVertex3D(RAdeg, DEdeg+arm_len_deg, radius);
    			sphVertex3D(RAdeg, DEdeg, radius);
    			// West/East arm:
    			sphVertex3D(RAdeg-arm_len_deg, DEdeg, radius);
    		glEnd();
    		
    		// arm joint
    		glPointSize((GLfloat) lineSize);
    		glBegin(GL_POINTS);
    			sphVertex3D(RAdeg, DEdeg, radius);
    		glEnd();
    	
    	glEndList();
    
    	return;
    }
    
    void Starsphere::make_search_marker(GLfloat RAdeg, GLfloat DEdeg, GLfloat size)
    {
    	GLfloat x, y;
    	GLfloat r1, r2, r3;
    	float theta;
    	int i, Nstep=20;
    
    	// r1 is inner circle, r2 is outer circle, r3 is crosshairs
    	r1 = size, r2=3*size, r3=4*size;
    	
    	// delete existing, create new (required for windoze)
    	if(SearchMarker) glDeleteLists(SearchMarker, 1);
    	SearchMarker = glGenLists(1);
    	glNewList(SearchMarker, GL_COMPILE);
    
    		// start gunsight drawing
    		glPushMatrix();
    		
    		glLineWidth(3.0);
    		glColor3f(1.0, 0.5, 0.0); // Orange
    		
    		// First rotate east  to the RA position around y
    		glRotatef(RAdeg, 0.0, 1.0, 0.0);
    		// Then rotate up to DEC position around z (not x)
    		glRotatef(DEdeg, 0.0, 0.0, 1.0);
    	
    		// Inner circle
    		glBegin(GL_LINE_LOOP);
    			for (i=0; i<Nstep; i++) {
    				theta = i*360.0/Nstep;
    				x = r1*COS(theta);
    				y = r1*SIN(theta);
    				sphVertex(x, y);
    			}
    		glEnd();
    	
    		// Outer circle
    		glBegin(GL_LINE_LOOP);
    			for (i=0; i<Nstep; i++) {
    				theta = i*360.0/Nstep;
    				x = r2*COS(theta);
    				y = r2*SIN(theta);
    				sphVertex(x, y);
    			}
    		glEnd();
    	
    		// Arms that form the gunsight
    		glBegin(GL_LINES);
    			//  North arm:
    			sphVertex(0.0, +r1);
    			sphVertex(0.0, +r3);
    			//  South arm:
    			sphVertex(0.0, -r1);
    			sphVertex(0.0, -r3);
    			// East arm:
    			sphVertex(-r1, 0.0);
    			sphVertex(-r3, 0.0);
    			// West arm:
    			sphVertex(+r1, 0.0);
    			sphVertex(+r3, 0.0);
    		glEnd();
    		
    		glPopMatrix();
    		
    		// searchlight line out to marker (OFF!)
    		if(false) {
    			glBegin(GL_LINES);
    				sphVertex3D(RAdeg, DEdeg, 0.50*sphRadius);
    				sphVertex3D(RAdeg, DEdeg, 0.95*sphRadius);
    			glEnd();
    		}
    
    	glEndList();
    }
    
    
    /**
     * XYZ coordinate axes: (if we want them - most useful for testing)
     */
    void Starsphere::make_axes()
    {
    	GLfloat axl=10.0;
    
    	// delete existing, create new (required for windoze)
    	if(Axes) glDeleteLists(Axes, 1);
    	Axes = glGenLists(1);
    	glNewList(Axes, GL_COMPILE);
    
    		glBegin(GL_LINES);
    			glLineWidth(2.0);
    			
    			glColor3f(1.0, 0.0, 0.0);
    			glVertex3f(-axl, 0.0, 0.0);
    			glVertex3f(axl, 0.0, 0.0);
    		
    			glColor3f(0.0, 1.0, 0.0);
    			glVertex3f(0.0, -axl, 0.0);
    			glVertex3f(0.0, axl, 0.0);
    		
    			glColor3f(0.0, 0.0, 1.0);
    			glVertex3f(0.0, 0.0, -axl);
    			glVertex3f(0.0, 0.0, axl);
    		glEnd();
    	
    	glEndList();
    }
    
    /**
     * RA/DEC coordinate grid on the sphere
     */
    void Starsphere::make_globe()
    {
    	int hr, j, i, iMax=100;
    	GLfloat RAdeg, DEdeg;
    
    	// delete existing, create new (required for windoze)
    	if(sphGrid) glDeleteLists(sphGrid, 1);
    	sphGrid = glGenLists(1);
    	glNewList(sphGrid, GL_COMPILE);
    
    		glLineWidth(1.0);
    		
    		// Lines of constant Right Ascencion (East Longitude)
    		for (hr=0; hr<24; hr++) {
    			RAdeg=hr*15.0;
    			glColor3f(0.25, 0.25, 0.25);
    			
    			// mark median
    			if(hr==0) glColor3f(0.55, 0.55, 0.55);
    			
    			glBegin(GL_LINE_STRIP);
    				for (i=0; i<=iMax; i++) {
    					DEdeg = i*180.0/iMax - 90.0;
    					sphVertex(RAdeg, DEdeg);
    				}
    			glEnd();
    		}
    	
    		// Lines of constant Declination (Lattitude)	
    		for (j=1; j<=12; j++) {
    			DEdeg = 90.0 - j*15.0;
    			
    			glBegin(GL_LINE_STRIP);
    				for (i=0; i<=iMax; i++) {
    					RAdeg = i*360.0/iMax;
    					sphVertex(RAdeg, DEdeg);
    				}
    			glEnd();
    		}
    		
    	glEndList();
    }
    
    /**
     * Window resize/remap
     */
    void Starsphere::resize(const int width, const int height)
    {
    	// store current settings
    	m_CurrentWidth = width;
    	m_CurrentHeight = height;
    	aspect = (float)width / (float)height;
    	
    	// adjust HUD config
    	m_YStartPosTop = height - 25;
    
    	// make sure the search marker is updated (conditional rendering!)
    	m_RefreshSearchMarker = true;
    	
    	// adjust aspect ratio and projection
    	glViewport(0, 0, (GLsizei) width, (GLsizei) height);
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	gluPerspective(95.0, aspect, 0.50, 25.0);
    	glMatrixMode(GL_MODELVIEW);
    }
    
    /**
     *  What to do when graphics are "initialized".
     */
    void Starsphere::initialize(const int width, const int height, const Resource *font, const bool recycle)
    {
    	// check whether we initialize the first time or have to recycle (required for windoze)
    	if(!recycle) {
    		
    		// store the font resource
    		if(font) m_FontResource = font;
    		
    		// initialize the BOINC client adapter
    		m_BoincAdapter.initialize("EinsteinHS");
    		
    		// inital HUD offset setup
    		m_XStartPosLeft = 5;
    		m_YOffsetLarge = 18;
    		
    		setFeature(STARS, true);
    		setFeature(CONSTELLATIONS, true);
    		setFeature(PULSARS, true);
    		setFeature(OBSERVATORIES, true);
    		setFeature(SNRS, true);
    		setFeature(GLOBE, true);
    		setFeature(SEARCHINFO, true);
    		setFeature(LOGO, true);
    		setFeature(MARKER, true);
    	}
    	else {
    		
    		// seems that windoze also "resets" our OpenGL fonts
    		// let's clean up before reinitializing them
    		if(m_FontLogo1) delete m_FontLogo1;
    		if(m_FontLogo2) delete m_FontLogo2;
    		if(m_FontHeader)delete m_FontHeader;
    		if(m_FontText)  delete m_FontText;
    	}
    
    	// we might be called to recycle even before initialization
    	if(!m_FontResource) {
    		
    		// display a warning, this could be unintentionally
    		cerr << "Warning: font resource still unknown! You might want to recycle at a later stage..." << endl;
    	}
    	else {
    		
    		// create large font instances using font resource (base address + size)
    		m_FontLogo1 = new OGLFT::TranslucentTexture(
    									&m_FontResource->data()->at(0),
    									m_FontResource->data()->size(),
    									24, 72 );
    		
    		if ( m_FontLogo1 == 0 || !m_FontLogo1->isValid() ) {
    		     cerr << "Could not construct logo1 font face from in memory resource!" << endl;
    		     return;
    		}
    		
    		m_FontLogo1->setForegroundColor(1.0, 1.0, 0.0, 1.0);
    		
    		// create medium font instances using font resource (base address + size)
    		m_FontLogo2 = new OGLFT::TranslucentTexture(
    									&m_FontResource->data()->at(0),
    									m_FontResource->data()->size(),
    									13, 78 );
    		
    		if ( m_FontLogo2 == 0 || !m_FontLogo2->isValid() ) {
    		     cerr << "Could not construct logo2 font face from in memory resource!" << endl;
    		     return;
    		}
    		
    		m_FontLogo2->setForegroundColor(0.75, 0.75, 0.75, 1.0);
    		
    		// create medium font instances using font resource (base address + size)
    		m_FontHeader = new OGLFT::TranslucentTexture(
    									&m_FontResource->data()->at(0),
    									m_FontResource->data()->size(),
    									13, 78 );
    		
    		if ( m_FontHeader == 0 || !m_FontHeader->isValid() ) {
    		     cerr << "Could not construct header font face from in memory resource!" << endl;
    		     return;
    		}
    		
    		m_FontHeader->setForegroundColor(1.0, 1.0, 0.0, 1.0);
    			
    		// create small font instances using font resource (base address + size)
    		m_FontText = new OGLFT::TranslucentTexture(
    									&m_FontResource->data()->at(0),
    									m_FontResource->data()->size(),
    									11, 72 );
    		
    		if ( m_FontText == 0 || !m_FontText->isValid() ) {
    		     cerr << "Could not construct text font face from in memory resource!" << endl;
    		     return;
    		}
    		
    		m_FontText->setForegroundColor(0.75, 0.75, 0.75, 1.0);
    	}
    	
    	// setup initial dimensions
    	resize(width, height);
    
    	// more font setup and optimizations
    	glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
    #if defined( GL_RASTER_POSITION_UNCLIPPED_IBM )
    	glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM );
    #endif
    	
    	// drawing setup:
    	glClearColor(0.0, 0.0, 0.0, 0.0); // background is black
    	glEnable(GL_CULL_FACE);
    	glFrontFace(GL_CCW);
    
    	// some polishing
    	glShadeModel(GL_SMOOTH);
    	glEnable(GL_POINT_SMOOTH);
    	glEnable(GL_LINE_SMOOTH);
    	
    	// FSAA will be enabled explicitly when needed!
    	glDisable(GL_MULTISAMPLE_ARB);
    
    	glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    	glHint(GL_FOG_HINT, GL_DONT_CARE);
    	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    
    	glEnable(GL_BLEND);
    	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    	// enable depth buffering for 3D graphics
    	glClearDepth(1.0f);
    	glEnable(GL_DEPTH_TEST);
    	glDepthFunc(GL_LEQUAL);
    
    	// fog aids depth perception
    	glEnable(GL_FOG);
    	glFogi(GL_FOG_MODE, GL_EXP2);
    	glFogf(GL_FOG_DENSITY, 0.085);
    
    	// create pre-drawn display lists
    	make_stars();
    	make_constellations();
    	make_pulsars();
    	make_snrs();
    	make_axes();
    	make_globe();
    	make_obs();
    
    	glDisable(GL_CLIP_PLANE0);
    	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    	glFlush();
    }
    
    /**
     * Rendering routine:  this is what does the drawing:   
     */
    void Starsphere::render(const double timeOfDay)
    {
    	GLfloat xvp, yvp, zvp, vp_theta, vp_phi, vp_rad;
    	GLfloat Zrot = 0.0, Zobs=0.0;
    	double revs, t, dt = 0;
    	static double start_time=-1.0, last_time=-1.0;
    	
    	// Calculate the real time t since we started (or reset) and the
    	// time dt since the last render() call.    Both may be useful
    	// for timing animations.  Note that time_of_day is dtime().
    
    	if (start_time < 0.0)
    		start_time = timeOfDay;
    	t = timeOfDay - start_time;
    
    	if (last_time < 0.0)
    		last_time = timeOfDay - 0.01;
    	dt = timeOfDay - last_time;
    
    	last_time = timeOfDay; // remember for next time
    
    	// Now determine the rotation angle based on the time since start
    	// It is negative to get the rotation direction correct (the sun
    	// rises in the East, so the sky sphere rotates E to W).
    
    	Zrot = t*rotation_speed/60.0;
    	revs = Zrot/360.0;
    	Zrot = -360.0 * (revs - (int)revs);
    
    	// and start drawing...
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    	// now draw the scene...
    	glLoadIdentity();
    
    	// Vary the viewpoint with both a long period wobble of the elevation 
    	// of the view and a longer period zoom in/out that might even penetrate
    	// The starsphere for a brief time.   Increase the power in pow(,) to 
    	// make the visit inside briefer.
    	vp_theta = 90.0 - viewpt_elev + wobble_amp*sin(PI2*t/(wobble_period*60.0));
    	vp_phi = viewpt_azimuth;
    	vp_rad = viewpt_radius - zoom_amp*sin(PI2*t/(zoom_period*60.0));
    	if(vp_rad <0.0) vp_rad = 0.0; // cannot pass origin (confusing)
    
    	// TRIED THIS TOO: -zoom_amp*pow(fabs(sin(PI2*t/(zoom_period*60.0))),3);
    	xvp = vp_rad * SIN(vp_theta) * SIN(vp_phi);
    	zvp = vp_rad * SIN(vp_theta) * COS(vp_phi);
    	yvp = vp_rad * COS(vp_theta);
    
    	gluLookAt(xvp, yvp, zvp, // eyes position
    	        0.0, 0.0, 0.0, // looking toward here
    	        0.0, 1.0, 0.0); // which way is up?  y axis!
    
    	// draw axes before any rotation so they stay put
    	if (isFeature(AXES)) glCallList(Axes);
    
    	// draw the sky sphere, with rotation:
    	glPushMatrix();
    	glRotatef(Zrot - rotation_offset, 0.0, 1.0, 0.0);
    
    	// stars, pulsars, supernovae, grid
    	if (isFeature(STARS))		glCallList(Stars);
    	if (isFeature(PULSARS))		glCallList(Pulsars);
    	if (isFeature(SNRS))		glCallList(SNRs);
    	if (isFeature(CONSTELLATIONS))	glCallList(Constellations);
    	if (isFeature(GLOBE))		glCallList(sphGrid);
    
    	// observatories move an extra 15 degrees/hr since they were drawn
    	if (isFeature(OBSERVATORIES)) {
    		glPushMatrix();
    		Zobs = (timeOfDay - m_ObservatoryDrawTimeLocal) * 15.0/3600.0;
    		glRotatef(Zobs, 0.0, 1.0, 0.0);
    		glCallList(LLOmarker);
    		glCallList(LHOmarker);
    		glCallList(GEOmarker);
    		glCallList(VIRGOmarker);
    		glPopMatrix();
    	}
    	
    	// draw the search marker (gunsight)
    	if (isFeature(MARKER)) {
    		if(m_RefreshSearchMarker) {
    			make_search_marker(m_CurrentRightAscension, m_CurrentDeclination, 0.5);
    			m_RefreshSearchMarker = false;
    		}
    		else {
    			glCallList(SearchMarker);
    		}
    	}
    
    	glPopMatrix();
    
    	// draw 2D vectorized HUD
    	if(isFeature(LOGO) || isFeature(SEARCHINFO)) {
    	
    		// disable depth testing since we're in 2D mode
    		glDisable(GL_DEPTH_TEST);
    		
    		// enable textured fonts
    		glEnable(GL_TEXTURE_2D);
    		
    		// save current state
    		glMatrixMode(GL_PROJECTION);
    		glPushMatrix();
    		glLoadIdentity();
    		glOrtho(0, m_CurrentWidth, 0, m_CurrentHeight, -1, 1);
    		glMatrixMode(GL_MODELVIEW);
    		glPushMatrix();
    		glLoadIdentity();
    	
    		if (isFeature(LOGO)) {
    			m_FontLogo1->draw(m_XStartPosLeft, m_YStartPosTop, "Einstein@Home");
    			m_FontLogo2->draw(m_XStartPosLeft, m_YStartPosTop - m_YOffsetLarge, "World Year of Physics 2005");
    		}
    		
    		if (isFeature(SEARCHINFO)) renderSearchInformation();
    		
    		// restore original state
    		glMatrixMode(GL_PROJECTION);
    		glPopMatrix();
    		glMatrixMode(GL_MODELVIEW);
    		glPopMatrix();
    		
    		// disable font textures
    		glDisable(GL_TEXTURE_2D);
    		
    		// enable depth testing since we're leaving 2D mode
    		glEnable(GL_DEPTH_TEST);
    	}
    
    	glFlush();
    	SDL_GL_SwapBuffers();
    }
    
    void Starsphere::mouseButtonEvent(const int positionX, const int positionY,
    								  const AbstractGraphicsEngine::MouseButton buttonPressed)
    {
    	
    }
    
    void Starsphere::mouseMoveEvent(const int deltaX, const int deltaY,
    								const AbstractGraphicsEngine::MouseButton buttonPressed)
    {
    	switch(buttonPressed) {
    		case MouseButtonLeft:
    			rotateSphere(deltaX, deltaY);
    			break;
    		case MouseButtonRight:
    			zoomSphere(deltaY);
    			break;
    		default:
    			break;
    	}
    }
    
    void Starsphere::keyboardPressEvent(const AbstractGraphicsEngine::KeyBoardKey keyPressed)
    {
    	switch(keyPressed) {
    		case KeyS:
    			setFeature(STARS, isFeature(STARS) ? false : true);
    			break;
    		case KeyC:
    			setFeature(CONSTELLATIONS, isFeature(CONSTELLATIONS) ? false : true);
    			break;
    		case KeyO:
    			setFeature(OBSERVATORIES, isFeature(OBSERVATORIES) ? false : true);
    			break;
    		case KeyX:
    			setFeature(XRAYS, isFeature(XRAYS) ? false : true);
    			break;
    		case KeyP:
    			setFeature(PULSARS, isFeature(PULSARS) ? false : true);
    			break;
    		case KeyR:
    			setFeature(SNRS, isFeature(SNRS) ? false : true);
    			break;
    		case KeyG:
    			setFeature(GLOBE, isFeature(GLOBE) ? false : true);
    			break;
    		case KeyA:
    			setFeature(AXES, isFeature(AXES) ? false : true);
    			break;
    		case KeyI:
    			setFeature(SEARCHINFO, isFeature(SEARCHINFO) ? false : true);
    			break;
    		case KeyL:
    			setFeature(LOGO, isFeature(LOGO) ? false : true);
    			break;
    		case KeyM:
    			setFeature(MARKER, isFeature(MARKER) ? false : true);
    			break;
    		default:
    			break;
    	}	
    }
    
    /**
     * View control
     */
    void Starsphere::rotateSphere(const int relativeRotation,
            const int relativeElevation)
    {
    	// elevation
    	viewpt_elev += relativeElevation/10.0;
    	if (viewpt_elev > 89.0)
    		viewpt_elev = +89.0;
    	if (viewpt_elev < -89.0)
    		viewpt_elev = -89.0;
    
    	// rotation
    	rotation_offset -= relativeRotation/10.0;
    }
    
    void Starsphere::zoomSphere(const int relativeZoom)
    {
    	// zoom
    	viewpt_radius -= relativeZoom/10.0;
    	if (viewpt_radius > 15.0)
    		viewpt_radius = 15.0;
    	if (viewpt_radius < 0.5)
    		viewpt_radius = 0.5;
    }
    
    /**
     * Feature control
     */
    void Starsphere::setFeature(const Features feature, const bool enable)
    {
    	featureFlags = enable ? (featureFlags | feature) : (featureFlags & ~feature);
    }
    
    bool Starsphere::isFeature(const Features feature)
    {
    	return ((featureFlags & feature) == feature ? true : false);
    }
    
    void Starsphere::refreshLocalBOINCInformation()
    {
    	// call base class implementation
    	AbstractGraphicsEngine::refreshLocalBOINCInformation();
    	
    	// prepare conversion buffer
    	stringstream buffer;
    	buffer.precision(2);
    	buffer.setf(ios::fixed, ios::floatfield);
    	buffer.fill('0');
    	buffer.setf(ios::right, ios::adjustfield);
    	
    	// store content required for our HUD (user info)
    	m_UserName = "User: " + m_BoincAdapter.userName();
    	m_TeamName = "Team: " + m_BoincAdapter.teamName();
    	
    	buffer << "Project Credit: " << fixed << m_BoincAdapter.userCredit() << ends;
    	m_UserCredit = buffer.str();
    	buffer.str("");
    	
    	buffer << "Project RAC: " << fixed << m_BoincAdapter.userRACredit() << ends;
    	m_UserRACredit = buffer.str();
    	buffer.str("");
    }