2008-03-20 15:36:17 -06:00
|
|
|
// planetgrid.cpp
|
|
|
|
//
|
|
|
|
// Longitude/latitude grids for ellipsoidal bodies.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2008, the Celestia Development Team
|
|
|
|
// Initial version by Chris Laurel, claurel@gmail.com
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cmath>
|
|
|
|
#include <celmath/intersect.h>
|
|
|
|
#include "planetgrid.h"
|
|
|
|
#include "body.h"
|
|
|
|
#include "gl.h"
|
|
|
|
#include "vecgl.h"
|
|
|
|
#include "render.h"
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int PlanetographicGrid::circleSubdivisions = 100;
|
|
|
|
float* PlanetographicGrid::xyCircle = NULL;
|
|
|
|
float* PlanetographicGrid::xzCircle = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
PlanetographicGrid::PlanetographicGrid(const Body& _body) :
|
|
|
|
body(_body),
|
|
|
|
minLongitudeStep(10.0f),
|
|
|
|
minLatitudeStep(10.0f),
|
|
|
|
longitudeConvention(Westward),
|
|
|
|
northDirection(NorthNormal)
|
|
|
|
{
|
|
|
|
if (xyCircle == NULL)
|
|
|
|
InitializeGeometry();
|
|
|
|
setTag("planetographic grid");
|
|
|
|
setIAULongLatConvention();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PlanetographicGrid::~PlanetographicGrid()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void longLatLabel(const string& labelText,
|
|
|
|
double longitude,
|
|
|
|
double latitude,
|
|
|
|
const Point3d& viewRayOrigin,
|
|
|
|
const Point3d& bodyPos,
|
|
|
|
const Quatd& bodyOrientation,
|
|
|
|
const Vec3f& semiAxes,
|
2008-03-24 11:45:04 -06:00
|
|
|
float labelOffset,
|
2008-03-20 15:36:17 -06:00
|
|
|
Renderer* renderer)
|
|
|
|
{
|
|
|
|
double theta = degToRad(longitude);
|
|
|
|
double phi = degToRad(latitude);
|
|
|
|
Vec3d pos(cos(phi) * cos(theta) * semiAxes.x,
|
|
|
|
sin(phi) * semiAxes.y,
|
|
|
|
-cos(phi) * sin(theta) * semiAxes.z);
|
|
|
|
|
2008-03-24 11:45:04 -06:00
|
|
|
pos = pos * (1.0 + labelOffset);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
// Draw the label only if it isn't obscured by the body ellipsoid
|
|
|
|
double t = 0.0;
|
|
|
|
if (testIntersection(Ray3d(viewRayOrigin, Point3d(pos.x, pos.y, pos.z) - viewRayOrigin),
|
|
|
|
Ellipsoidd(Vec3d(semiAxes.x, semiAxes.y, semiAxes.z)), t) && t >= 1.0)
|
|
|
|
{
|
2008-03-24 11:45:04 -06:00
|
|
|
Point3d labelPos = bodyPos + (1.0 + labelOffset) * pos * bodyOrientation.toMatrix3();
|
2008-03-20 15:36:17 -06:00
|
|
|
renderer->addForegroundAnnotation(NULL, labelText,
|
|
|
|
Renderer::PlanetographicGridLabelColor,
|
|
|
|
Point3f((float) labelPos.x, (float) labelPos.y, (float) labelPos.z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PlanetographicGrid::render(Renderer* renderer,
|
|
|
|
const Point3f& pos,
|
|
|
|
float discSizeInPixels,
|
|
|
|
double tdb) const
|
|
|
|
{
|
|
|
|
Quatd q = Quatd::yrotation(PI) * body.getEclipticToBodyFixed(tdb);
|
|
|
|
Quatf qf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
|
|
|
|
|
2008-03-24 11:45:04 -06:00
|
|
|
float offset = max(0.0005f, min(0.01f, 3.0f / discSizeInPixels));
|
|
|
|
float scale = 1.0f + offset;
|
2008-03-20 15:36:17 -06:00
|
|
|
Vec3f semiAxes = body.getSemiAxes();
|
|
|
|
|
|
|
|
Point3d posd(pos.x, pos.y, pos.z);
|
|
|
|
Point3d viewRayOrigin = Point3d(-pos.x, -pos.y, -pos.z) * (~q).toMatrix3();
|
|
|
|
|
|
|
|
// Enable depth buffering
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
glPushMatrix();
|
|
|
|
glRotate(~qf);
|
|
|
|
glScale(scale * semiAxes);
|
|
|
|
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
2008-03-24 11:45:04 -06:00
|
|
|
glVertexPointer(3, GL_FLOAT, 0, xzCircle);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
// Only show the coordinate labels if the body is sufficiently large on screen
|
|
|
|
bool showCoordinateLabels = false;
|
|
|
|
if (discSizeInPixels > 50)
|
|
|
|
showCoordinateLabels = true;
|
|
|
|
|
|
|
|
float latitudeStep = minLatitudeStep;
|
|
|
|
float longitudeStep = minLongitudeStep;
|
|
|
|
if (discSizeInPixels < 200)
|
|
|
|
{
|
|
|
|
latitudeStep = 30.0f;
|
|
|
|
longitudeStep = 30.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (float latitude = -90.0f + latitudeStep; latitude < 90.0f; latitude += latitudeStep)
|
|
|
|
{
|
|
|
|
float phi = degToRad(latitude);
|
|
|
|
float r = (float) cos(phi);
|
|
|
|
|
|
|
|
if (latitude == 0.0f)
|
|
|
|
{
|
|
|
|
glColor(Renderer::PlanetEquatorColor);
|
|
|
|
glLineWidth(2.0f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glColor(Renderer::PlanetographicGridColor);
|
|
|
|
}
|
|
|
|
glPushMatrix();
|
|
|
|
glTranslatef(0.0f, (float) sin(phi), 0.0f);
|
|
|
|
glScalef(r, r, r);
|
|
|
|
glDrawArrays(GL_LINE_LOOP, 0, circleSubdivisions);
|
|
|
|
glPopMatrix();
|
|
|
|
glLineWidth(1.0f);
|
|
|
|
|
|
|
|
if (showCoordinateLabels)
|
|
|
|
{
|
|
|
|
if (latitude != 0.0f && abs(latitude) < 90.0f)
|
|
|
|
{
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
char ns;
|
|
|
|
if (latitude < 0.0f)
|
|
|
|
ns = northDirection == NorthNormal ? 'S' : 'N';
|
|
|
|
else
|
|
|
|
ns = northDirection == NorthNormal ? 'N' : 'S';
|
|
|
|
sprintf(buf, "%d%c", (int) fabs((double) latitude), ns);
|
2008-03-24 11:45:04 -06:00
|
|
|
longLatLabel(buf, 0.0, latitude, viewRayOrigin, posd, q, semiAxes, offset, renderer);
|
|
|
|
longLatLabel(buf, 180.0, latitude, viewRayOrigin, posd, q, semiAxes, offset, renderer);
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-24 11:45:04 -06:00
|
|
|
glVertexPointer(3, GL_FLOAT, 0, xyCircle);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
for (float longitude = 0.0f; longitude <= 180.0f; longitude += longitudeStep)
|
|
|
|
{
|
|
|
|
glColor(Renderer::PlanetographicGridColor);
|
|
|
|
glPushMatrix();
|
|
|
|
glRotatef(longitude, 0.0f, 1.0f, 0.0f);
|
|
|
|
glDrawArrays(GL_LINE_LOOP, 0, circleSubdivisions);
|
|
|
|
glPopMatrix();
|
|
|
|
|
|
|
|
if (showCoordinateLabels)
|
|
|
|
{
|
|
|
|
int showLongitude = 0;
|
|
|
|
char ew = 'E';
|
|
|
|
|
|
|
|
switch (longitudeConvention)
|
|
|
|
{
|
|
|
|
case EastWest:
|
|
|
|
ew = 'E';
|
|
|
|
showLongitude = (int) longitude;
|
|
|
|
break;
|
|
|
|
case Eastward:
|
|
|
|
if (longitude > 0.0f)
|
|
|
|
showLongitude = 360 - (int) longitude;
|
|
|
|
ew = 'E';
|
|
|
|
break;
|
|
|
|
case Westward:
|
|
|
|
if (longitude > 0.0f)
|
|
|
|
showLongitude = 360 - (int) longitude;
|
|
|
|
ew = 'W';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[64];
|
|
|
|
sprintf(buf, "%d%c", (int) showLongitude, ew);
|
2008-03-24 11:45:04 -06:00
|
|
|
longLatLabel(buf, longitude, 0.0, viewRayOrigin, posd, q, semiAxes, offset, renderer);
|
2008-03-20 15:36:17 -06:00
|
|
|
if (longitude > 0.0f && longitude < 180.0f)
|
|
|
|
{
|
|
|
|
showLongitude = (int) longitude;
|
|
|
|
switch (longitudeConvention)
|
|
|
|
{
|
|
|
|
case EastWest:
|
|
|
|
ew = 'W';
|
|
|
|
showLongitude = (int) longitude;
|
|
|
|
break;
|
|
|
|
case Eastward:
|
|
|
|
showLongitude = (int) longitude;
|
|
|
|
ew = 'E';
|
|
|
|
break;
|
|
|
|
case Westward:
|
|
|
|
showLongitude = (int) longitude;
|
|
|
|
ew = 'W';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(buf, "%d%c", showLongitude, ew);
|
2008-03-24 11:45:04 -06:00
|
|
|
longLatLabel(buf, -longitude, 0.0, viewRayOrigin, posd, q, semiAxes, offset, renderer);
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
PlanetographicGrid::boundingSphereRadius() const
|
|
|
|
{
|
|
|
|
return body.getRadius();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*! Determine the longitude convention to use based on IAU rules:
|
|
|
|
* Westward for prograde rotators, Eastward for retrograde
|
|
|
|
* rotators, EastWest for the Earth and Moon.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
PlanetographicGrid::setIAULongLatConvention()
|
|
|
|
{
|
|
|
|
if (body.getName() == "Earth" || body.getName() == "Moon")
|
|
|
|
{
|
|
|
|
northDirection = NorthNormal;
|
|
|
|
longitudeConvention = EastWest;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (body.getAngularVelocity(astro::J2000).y >= 0.0)
|
|
|
|
{
|
|
|
|
northDirection = NorthNormal;
|
|
|
|
longitudeConvention = Westward;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
northDirection = NorthReversed;
|
|
|
|
longitudeConvention = Eastward;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PlanetographicGrid::InitializeGeometry()
|
|
|
|
{
|
|
|
|
xyCircle = new float[circleSubdivisions * 3];
|
|
|
|
xzCircle = new float[circleSubdivisions * 3];
|
|
|
|
for (unsigned int i = 0; i < circleSubdivisions; i++)
|
|
|
|
{
|
|
|
|
float theta = (float) (2.0 * PI) * (float) i / (float) circleSubdivisions;
|
|
|
|
xyCircle[i * 3 + 0] = (float) cos(theta);
|
|
|
|
xyCircle[i * 3 + 1] = (float) sin(theta);
|
|
|
|
xyCircle[i * 3 + 2] = 0.0f;
|
|
|
|
xzCircle[i * 3 + 0] = (float) cos(theta);
|
|
|
|
xzCircle[i * 3 + 1] = 0.0f;
|
|
|
|
xzCircle[i * 3 + 2] = (float) sin(theta);
|
|
|
|
}
|
|
|
|
}
|